This commit is contained in:
lx
2026-07-06 14:01:11 -04:00
commit c4f97d729d
16468 changed files with 935321 additions and 0 deletions
@@ -0,0 +1,8 @@
{
"language": {
"mode": "nocheck"
},
"lint": {
"*": "disabled"
}
}
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["AvatarExperienceDeps"]["AvatarExperienceDeps"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["roblox_cryo"]["cryo"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent._Index
local package = PackageIndex["roblox_rhodium"]["rhodium"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent._Index
local package = PackageIndex["roblox_testez"]["testez"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["InGameMenuDependencies"]["InGameMenuDependencies"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["LuaChatDeps"]["LuaChatDeps"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["LuaDiscussionsDeps"]["LuaDiscussionsDeps"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["LuaSocialLibrariesDeps"]["LuaSocialLibrariesDeps"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["roblox_lumberyak"]["lumberyak"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["roblox_otter"]["otter"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["roblox_lua-roact-policy-provider"]["lua-roact-policy-provider"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["PremiumUpsellDeps"]["PremiumUpsellDeps"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["lua-promise"]["lua-promise"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["roblox_purchase-prompt"]["purchase-prompt"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["roblox_lua-result"]["lua-result"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["roblox_roact"]["roact"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["roblox_roact-gamepad"]["roact-gamepad"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["roblox_roact-navigation"]["roact-navigation"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["roblox_roact-rodux"]["roact-rodux"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["roblox_rodux"]["rodux"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["roblox_string-utilities"]["string-utilities"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["UIBlox"]["UIBlox"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent._Index
local package = PackageIndex["roblox_url-builder"]["url-builder"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,5 @@
local AvatarExperineceDeps = script.Parent
return {
RoactFitComponents = require(AvatarExperineceDeps.RoactFitComponents),
}
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_roact-fit-components"]["roact-fit-components"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,6 @@
# Generated by Rotriever. Format subject to change in future releases.
name = "AvatarExperienceDeps"
version = "0.0.1"
commit = "5a31b41867d2c5ebf0b5670d9be27f3edd403545"
source = "git+https://github.com/roblox/avatar-experience-deps#master"
dependencies = ["RoactFitComponents roblox/roact-fit-components 1.2.5 url+https://github.com/roblox/roact-fit-components"]
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_cryo"]["cryo"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
local InGameMenuDependencies = script.Parent
return {
Roact = require(InGameMenuDependencies.Roact),
Rodux = require(InGameMenuDependencies.Rodux),
RoactRodux = require(InGameMenuDependencies.RoactRodux),
UIBlox = require(InGameMenuDependencies.UIBlox),
Otter = require(InGameMenuDependencies.Otter),
Cryo = require(InGameMenuDependencies.Cryo),
t = require(InGameMenuDependencies.t),
PolicyProvider = require(InGameMenuDependencies.PolicyProvider),
}
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_otter"]["otter"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_lua-roact-policy-provider"]["lua-roact-policy-provider"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_roact"]["roact"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_roact-rodux"]["roact-rodux"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_rodux"]["rodux"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["UIBlox"]["UIBlox"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,15 @@
# Generated by Rotriever. Format subject to change in future releases.
name = "InGameMenuDependencies"
version = "0.1.0"
commit = "aecc56c1fa6348886a1073785dbb196b655a0a90"
source = "git+https://github.rbx.com/roblox/in-game-menu-dependencies#master"
dependencies = [
"Cryo roblox/cryo 1.0.0 url+https://github.com/roblox/cryo",
"Otter roblox/otter 0.1.2 url+https://github.com/roblox/otter",
"PolicyProvider roblox/lua-roact-policy-provider 1d595dae git+https://github.com/roblox/lua-roact-policy-provider#master",
"Roact roblox/roact 1.3.0 url+https://github.com/roblox/roact",
"RoactRodux roblox/roact-rodux 0.2.2 url+https://github.com/roblox/roact-rodux",
"Rodux roblox/rodux 1.0.0 url+https://github.com/roblox/rodux",
"UIBlox <patched> UIBlox a253d523 git+https://github.com/roblox/uiblox#master",
"t roblox/t 1.2.5 url+https://github.com/roblox/t",
]
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_t"]["t"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["asset-card"]["asset-card"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_infinite-scroller-98304e77-0.5.6"]["infinite-scroller"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,6 @@
return function()
it("SHOULD initialize UIBlox when required", function()
local LuaChatDeps = require(script.Parent)
expect(LuaChatDeps.UIBlox.Config).to.be.ok()
end)
end
@@ -0,0 +1,4 @@
return {
fixToastResizeConfig = true,
expandableTextAutomaticResizeConfig = true,
}
@@ -0,0 +1,14 @@
local LuaChatDeps = script.Parent
-- initialize UIBlox once here, there should be no other
-- consumers of this instance of UIBlox
local UIBloxConfig = require(script.config.UIBlox)
local UIBlox = require(LuaChatDeps.UIBlox)
UIBlox.init(UIBloxConfig)
return {
InfiniteScroll = require(LuaChatDeps.InfiniteScroller),
RoduxNetworking = require(LuaChatDeps.RoduxNetworking),
UIBlox = UIBlox,
AssetCard = require(LuaChatDeps.AssetCard),
}
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["rodux-networking"]["rodux-networking"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["UIBlox"]["UIBlox"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,11 @@
# Generated by Rotriever. Format subject to change in future releases.
name = "LuaChatDeps"
version = "0.1.3"
commit = "6144362fd181fe94fd213ec8569ba95f0a61faed"
source = "git+https://github.rbx.com/roblox/lua-chat-deps#master"
dependencies = [
"AssetCard asset-card c7b683fb git+https://github.com/roblox/asset-card#v1.0.2",
"InfiniteScroller roblox/infinite-scroller 0.5.6 url+https://github.com/roblox/infinite-scroller",
"RoduxNetworking rodux-networking ea19cfe3 git+https://github.rbx.com/roblox/rodux-networking#v1.0.1",
"UIBlox UIBlox a253d523 git+https://github.com/roblox/uiblox#master",
]
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_infinite-scroller-98304e77-0.3.4"]["infinite-scroller"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,8 @@
local LuaDiscussionsDeps = script.Parent
return {
InfiniteScroll = require(LuaDiscussionsDeps.InfiniteScroll),
RoactFitComponents = require(LuaDiscussionsDeps.RoactFitComponents),
RoactNavigation = require(LuaDiscussionsDeps.RoactNavigation),
RoduxNetworking = require(LuaDiscussionsDeps.RoduxNetworking),
}
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_roact-fit-components"]["roact-fit-components"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roact-navigation"]["roact-navigation"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["rodux-networking"]["rodux-networking"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,11 @@
# Generated by Rotriever. Format subject to change in future releases.
name = "LuaDiscussionsDeps"
version = "0.1.2"
commit = "dfba285e3dd4e0fe5059b93d52880666e2924612"
source = "git+https://github.rbx.com/roblox/lua-discussions-deps#master"
dependencies = [
"InfiniteScroll roblox/infinite-scroller 0.3.4 url+https://github.com/roblox/infinite-scroller",
"RoactFitComponents roblox/roact-fit-components 1.2.5 url+https://github.com/roblox/roact-fit-components",
"RoactNavigation roact-navigation 0.1.1-linter-fix url+https://github.com/roblox/roact-navigation",
"RoduxNetworking rodux-networking ea19cfe3 git+https://github.rbx.com/roblox/rodux-networking#v1.0.1",
]
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_genericpagination"]["genericpagination"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,7 @@
local LuaSocialLibrariesDeps = script.Parent
return {
GenericPagination = require(LuaSocialLibrariesDeps.GenericPagination),
RoactFitComponents = require(LuaSocialLibrariesDeps.RoactFitComponents),
Mock = require(LuaSocialLibrariesDeps.Mock),
}
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["jtaylor_mock"]["mock"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_roact-fit-components"]["roact-fit-components"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,10 @@
# Generated by Rotriever. Format subject to change in future releases.
name = "LuaSocialLibrariesDeps"
version = "0.1.1"
commit = "6d4213307f9d059e422026c5f8ad1a37a3489a9b"
source = "git+https://github.rbx.com/roblox/lua-social-libraries-deps#master"
dependencies = [
"GenericPagination roblox/genericpagination 6cc56178 git+https://github.rbx.com/roblox/genericpagination#master",
"Mock jtaylor/mock d2c4005c git+https://github.rbx.com/roblox/mock#master",
"RoactFitComponents roblox/roact-fit-components 1.2.5 url+https://github.com/roblox/roact-fit-components",
]
@@ -0,0 +1,5 @@
local PremiumUpsellDeps = script.Parent
return {
RoactFitComponents = require(PremiumUpsellDeps.RoactFitComponents),
}
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_roact-fit-components"]["roact-fit-components"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,6 @@
# Generated by Rotriever. Format subject to change in future releases.
name = "PremiumUpsellDeps"
version = "0.0.0"
commit = "c51030b70236f2e9d69bb34348b7e045c2c437f6"
source = "git+https://github.com/roblox/premium-upsell-deps#master"
dependencies = ["RoactFitComponents roblox/roact-fit-components 1.2.5 url+https://github.com/roblox/roact-fit-components"]
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_cryo"]["cryo"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_roact-fit-components"]["roact-fit-components"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_infinite-scroller-98304e77-0.5.6"]["infinite-scroller"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_otter"]["otter"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_roact"]["roact"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,12 @@
--[[
Package link auto-generated by Rotriever
]]
local PackageIndex = script.Parent.Parent
local package = PackageIndex["roblox_roact-gamepad"]["roact-gamepad"]
if package.ClassName == "ModuleScript" then
return require(package)
end
return package
@@ -0,0 +1,314 @@
local AccordionRoot = script.Parent
local AppRoot = AccordionRoot.Parent
local UIBloxRoot = AppRoot.Parent
local Packages = UIBloxRoot.Parent
local Roact = require(Packages.Roact)
local t = require(Packages.t)
local SpringAnimatedItem = require(UIBloxRoot.Utility.SpringAnimatedItem)
local ITEM_PADDING = 10
local ITEM_WIDTH_SHRINK_STEP = 20 -- How much each item shrinks below card above it
local COMPACT_VIEW_PLACEHOLDER_HEIGHT = 10
local PRESSED_SCALE = 0.9
local ANIMATION_SPRING_SETTINGS = {
dampingRatio = 1,
frequency = 3.5,
}
local AccordionView = Roact.PureComponent:extend("AccordionView")
AccordionView.defaultProps = {
maxItemsInCompactView = 3,
}
local validateProps = t.strictInterface({
items = t.table,
itemWidth = t.number,
itemHeight = t.number,
renderItem = t.callback,
placeholderColor = t.Color3,
placeholderBaseTransparency = t.number,
collapseButtonSize = t.number,
renderCollapseButton = t.callback,
LayoutOrder = t.optional(t.integer),
maxItemsInCompactView = t.numberPositive,
})
function AccordionView:init()
self.state = {
expanded = false,
isExpandButtonPressed = false,
}
self.onExpandButtonActivated = function()
self:setState({
expanded = true,
isExpandButtonPressed = false,
})
end
self.onCollapseButtonActivated = function()
self:setState({
expanded = false,
})
end
self.onExpandButtonInputBegan = function(_, inputObject)
if inputObject.UserInputState == Enum.UserInputState.Begin and
(inputObject.UserInputType == Enum.UserInputType.Touch or
inputObject.UserInputType == Enum.UserInputType.MouseButton1) then
self:setState({
isExpandButtonPressed = true,
})
end
end
self.onExpandButtonInputEnded = function()
if self.state.isExpandButtonPressed then
self:setState({
isExpandButtonPressed = false,
})
end
end
self.rootFrameRef = Roact.createRef()
self.onListLayoutAbsoluteContentSizeChanged = function(rbx)
if self.rootFrameRef.current then
local itemWidth = self.props.itemWidth
local minimumHeight = self:getCompactTotalHeight()
self.rootFrameRef.current.Size = UDim2.new(0, itemWidth,
0, math.max(rbx.AbsoluteContentSize.Y, minimumHeight))
end
end
end
function AccordionView:getCompactTotalHeight()
local items = self.props.items
local itemHeight = self.props.itemHeight
local maxItemsInCompactView = self.props.maxItemsInCompactView
local totalNumberOfItems = #items
if totalNumberOfItems == 0 then
return 0
else
return itemHeight + (math.min(maxItemsInCompactView, totalNumberOfItems) - 1) * COMPACT_VIEW_PLACEHOLDER_HEIGHT
end
end
function AccordionView:getLayoutInfo()
local items = self.props.items
local itemWidth = self.props.itemWidth
local itemHeight = self.props.itemHeight
local placeholderBaseTransparency = self.props.placeholderBaseTransparency
local maxItemsInCompactView = self.props.maxItemsInCompactView
local expanded = self.state.expanded
local layoutData = {}
local totalNumberOfItems = #items
local itemsShownInCompactView = math.min(maxItemsInCompactView, totalNumberOfItems)
local placeholderTransparencyStep = 0
if itemsShownInCompactView > 1 then
placeholderTransparencyStep = (1 - placeholderBaseTransparency) / (itemsShownInCompactView - 1)
end
for index = 1, totalNumberOfItems do
if expanded then
layoutData[index] = {
width = itemWidth,
height = itemHeight,
placeholderTransparency = 1,
itemTransparency = 0,
}
else
if index == 1 then
layoutData[index] = {
width = itemWidth,
height = itemHeight,
placeholderTransparency = 1,
itemTransparency = 0,
}
elseif index <= maxItemsInCompactView then
layoutData[index] = {
width = itemWidth - ITEM_WIDTH_SHRINK_STEP * (index - 1),
height = COMPACT_VIEW_PLACEHOLDER_HEIGHT,
placeholderTransparency = placeholderBaseTransparency + placeholderTransparencyStep * (index - 2),
itemTransparency = 1,
}
else
layoutData[index] = {
width = itemWidth - ITEM_WIDTH_SHRINK_STEP * (index - 1),
height = 0,
placeholderTransparency = 1,
itemTransparency = 1,
}
end
end
end
return layoutData
end
function AccordionView:render()
assert(validateProps(self.props))
local items = self.props.items
local totalNumberOfItems = #items
if totalNumberOfItems == 0 then
return nil
end
local layoutOrder = self.props.LayoutOrder
local itemWidth = self.props.itemWidth
local renderItem = self.props.renderItem
local placeholderColor = self.props.placeholderColor
local collapseButtonSize = self.props.collapseButtonSize
local renderCollapseButton = self.props.renderCollapseButton
local expanded = self.state.expanded
local isExpandButtonPressed = self.state.isExpandButtonPressed
local layoutData = self:getLayoutInfo()
local accordionContent = {
Layout = Roact.createElement("UIListLayout", {
FillDirection = Enum.FillDirection.Vertical,
HorizontalAlignment = Enum.HorizontalAlignment.Center,
SortOrder = Enum.SortOrder.LayoutOrder,
Padding = UDim.new(0, expanded and ITEM_PADDING or 0),
[Roact.Change.AbsoluteContentSize] = self.onListLayoutAbsoluteContentSizeChanged,
}),
Scaler = Roact.createElement(SpringAnimatedItem.AnimatedUIScale, {
springOptions = ANIMATION_SPRING_SETTINGS,
animatedValues = {
scale = isExpandButtonPressed and PRESSED_SCALE or 1,
},
mapValuesToProps = function(values)
return {
Scale = values.scale,
}
end,
}),
CollapseButton = Roact.createElement(SpringAnimatedItem.AnimatedFrame, {
springOptions = ANIMATION_SPRING_SETTINGS,
animatedValues = {
-- Increase the size by 1 pixel so the animation looks better
-- when the spring is damping in the end.
sizeOffsetY = expanded and collapseButtonSize + 1 or 0,
},
mapValuesToProps = function(values)
return {
Size = UDim2.new(0, collapseButtonSize, 0, values.sizeOffsetY),
}
end,
regularProps = {
BackgroundTransparency = 1,
ClipsDescendants = true,
[Roact.Children] = {
ButtonMoveContainer = Roact.createElement(SpringAnimatedItem.AnimatedFrame, {
springOptions = ANIMATION_SPRING_SETTINGS,
animatedValues = {
positionOffsetY = expanded and 0 or collapseButtonSize / 2,
},
mapValuesToProps = function(values)
return {
Position = UDim2.new(0, 0, 0, values.positionOffsetY),
}
end,
regularProps = {
Size = UDim2.new(0, collapseButtonSize, 0, collapseButtonSize),
BackgroundTransparency = 1,
[Roact.Children] = {
Button = renderCollapseButton(self.onCollapseButtonActivated),
},
},
}),
},
},
}),
}
for index, _ in ipairs(items) do
local layout = layoutData[index]
accordionContent["Item" .. tostring(index)] = Roact.createElement(SpringAnimatedItem.AnimatedFrame, {
springOptions = ANIMATION_SPRING_SETTINGS,
animatedValues = {
width = layout.width,
height = layout.height,
},
mapValuesToProps = function(values)
return {
Size = UDim2.new(0, values.width, 0, values.height),
}
end,
regularProps = {
Size = UDim2.new(1, 0, 0, 0),
BackgroundTransparency = 1,
BorderSizePixel = 0,
LayoutOrder = index + 1,
ZIndex = totalNumberOfItems + 1 - index;
ClipsDescendants = true,
[Roact.Children] = {
Item = renderItem(items[index], layout.itemTransparency, ANIMATION_SPRING_SETTINGS),
Placeholder = Roact.createElement(SpringAnimatedItem.AnimatedFrame, {
springOptions = ANIMATION_SPRING_SETTINGS,
animatedValues = {
transparency = layout.placeholderTransparency,
},
mapValuesToProps = function(values)
return {
BackgroundTransparency = values.transparency,
}
end,
regularProps = {
Size = UDim2.new(1, 0, 1, 0),
BackgroundColor3 = placeholderColor,
BorderSizePixel = 0,
},
}),
},
},
})
end
local canExpand = (totalNumberOfItems > 1)
local clickToExpand = canExpand and not expanded
return Roact.createElement("Frame", {
Size = UDim2.new(0, itemWidth, 0, 0),
BackgroundTransparency = 1,
BorderSizePixel = 0,
LayoutOrder = layoutOrder,
[Roact.Ref] = self.rootFrameRef,
}, {
ContentFrame = Roact.createElement("Frame", {
Size = UDim2.new(1, 0, 1, 0),
Position = UDim2.new(0.5, 0, 0.5, 0),
AnchorPoint = Vector2.new(0.5, 0.5),
BackgroundTransparency = 1,
},
accordionContent
),
ClickToExpandButton = clickToExpand and Roact.createElement("TextButton", {
Size = UDim2.new(1, 0, 1, 0),
BackgroundTransparency = 1,
BorderSizePixel = 0,
ZIndex = totalNumberOfItems + 1,
Text = "",
[Roact.Event.Activated] = self.onExpandButtonActivated,
[Roact.Event.InputBegan] = self.onExpandButtonInputBegan,
[Roact.Event.InputEnded] = self.onExpandButtonInputEnded,
}),
})
end
return AccordionView
@@ -0,0 +1,72 @@
return function()
local AccordionRoot = script.Parent
local AppRoot = AccordionRoot.Parent
local UIBloxRoot = AppRoot.Parent
local Packages = UIBloxRoot.Parent
local AccordionView = require(AccordionRoot.AccordionView)
local Roact = require(Packages.Roact)
describe("AccordionView", function()
it("should mount correctly", function()
local element = Roact.createElement(AccordionView, {
items = {"test", "test2"},
itemWidth = 355,
itemHeight = 188,
renderItem = function(item, transparency)
return Roact.createElement("TextLabel", {
Size = UDim2.new(1, 0, 1, 0),
Text = item,
BackgroundTransparency = transparency,
})
end,
placeholderColor = Color3.fromRGB(255, 255, 255),
placeholderBaseTransparency = 0.5,
collapseButtonSize = 40,
renderCollapseButton = function(activatedCallback)
return Roact.createElement("TextButton", {
Size = UDim2.new(1, 0, 1, 0),
BackgroundTransparency = 1,
Text = "close",
AutoButtonColor = false,
[Roact.Event.Activated] = activatedCallback,
})
end,
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
it("should mount correctly with empty items", function()
local element = Roact.createElement(AccordionView, {
items = {},
itemWidth = 355,
itemHeight = 188,
renderItem = function(item, transparency)
return Roact.createElement("TextLabel", {
Size = UDim2.new(1, 0, 1, 0),
Text = item,
BackgroundTransparency = transparency,
})
end,
placeholderColor = Color3.fromRGB(255, 255, 255),
placeholderBaseTransparency = 0.5,
collapseButtonSize = 40,
renderCollapseButton = function(activatedCallback)
return Roact.createElement("TextButton", {
Size = UDim2.new(1, 0, 1, 0),
BackgroundTransparency = 1,
Text = "close",
AutoButtonColor = false,
[Roact.Event.Activated] = activatedCallback,
})
end,
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
end)
end
@@ -0,0 +1,221 @@
local Bar = script.Parent
local App = Bar.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local Otter = require(Packages.Otter)
local t = require(Packages.t)
local IconButton = require(App.Button.IconButton)
local Images = require(App.ImageSet.Images)
local IconSize = require(App.ImageSet.Enum.IconSize)
local getIconSize = require(App.ImageSet.getIconSize)
local ControlState = require(Packages.UIBlox.Core.Control.Enum.ControlState)
local withStyle = require(UIBlox.Core.Style.withStyle)
local ThreeSectionBar = require(UIBlox.Core.Bar.ThreeSectionBar)
local lerp = require(Packages.UIBlox.Utility.lerp)
local divideTransparency = require(Packages.UIBlox.Utility.divideTransparency)
local TITLE_BAR_HEIGHT = 64
local SHADOW_HEIGHT = 24
local MARGIN = 20
local PADDING_BETWEEN = 12
local TITLE_BAR_OFF_POS = UDim2.new(0, 0, 0, -(TITLE_BAR_HEIGHT + SHADOW_HEIGHT))
local TITLE_BAR_ON_POS = UDim2.fromOffset(0, 0)
local EXIT_BUTTON_IMAGE_ID = "icons/actions/previewShrink"
local CLOSE_BUTTON_IMAGE_ID = "icons/navigation/close"
local GRADIENT_OPACITY = 0.25
local MOTOR_OPTIONS = {
frequency = 5,
}
local FullscreenTitleBar = Roact.PureComponent:extend("FullscreenTitleBar")
FullscreenTitleBar.validateProps = t.strictInterface({
title = t.string,
isTriggered = t.optional(t.boolean),
onDisappear = t.optional(t.callback),
exitFullscreen = t.optional(t.callback),
closeRoblox = t.optional(t.callback),
})
function FullscreenTitleBar:init()
local initProgress = self.props.isTriggered and 1 or 0
local setProgress
self.progress, setProgress = Roact.createBinding(initProgress)
self.exitControlState, self.setExitControlState = Roact.createBinding(self.state.exitControlState)
self.closeControlState, self.setCloseControlState = Roact.createBinding(self.state.closeControlState)
self.titleBarPosition = self.progress:map(function(value)
return TITLE_BAR_OFF_POS:lerp(TITLE_BAR_ON_POS, value)
end)
self.progressMotor = Otter.createSingleMotor(initProgress)
self.progressMotor:onStep(setProgress)
end
function FullscreenTitleBar:render()
return withStyle(function(style)
local theme = style.Theme
local font = style.Font
local backgroundStyle = theme.BackgroundUIDefault
local textColorStyle = theme.TextEmphasis
local centerTextFont = font.Header2
local centerTextSize = centerTextFont.RelativeSize * font.BaseSize
local titleBarTransparency = self.progress:map(function(value)
local baseTransparency = backgroundStyle.Transparency
return lerp(1, baseTransparency, value)
end)
local exitButtonTransparency = Roact.joinBindings({
progress = self.progress,
controlState = self.exitControlState,
}):map(function(values)
local baseTransparency = theme.ContextualPrimaryDefault.Transparency
local transparencyDivisor = values.controlState == ControlState.Pressed and 2 or 1
return lerp(1, divideTransparency(baseTransparency, transparencyDivisor), values.progress)
end)
local closeButtonTransparency = Roact.joinBindings({
progress = self.progress,
controlState = self.closeControlState,
}):map(function(values)
local baseTransparency = theme.ContextualPrimaryDefault.Transparency
local transparencyDivisor = values.controlState == ControlState.Pressed and 2 or 1
return lerp(1, divideTransparency(baseTransparency, transparencyDivisor), values.progress)
end)
local textTransparency = self.progress:map(function(value)
local baseTransparency = textColorStyle.Transparency
return lerp(1, baseTransparency, value)
end)
local function renderCenterText()
return Roact.createElement("TextLabel", {
BackgroundTransparency = 1,
Font = centerTextFont.Font,
Size = UDim2.new(1, 0, 0, centerTextSize),
Text = self.props.title,
TextColor3 = textColorStyle.Color,
TextSize = centerTextSize,
TextTransparency = textTransparency,
TextTruncate = Enum.TextTruncate.AtEnd,
TextWrapped = false,
})
end
local function renderRightButtons()
return Roact.createFragment({
ExitButton = Roact.createElement(IconButton, {
icon = Images[EXIT_BUTTON_IMAGE_ID],
iconSize = IconSize.Medium,
iconTransparency = exitButtonTransparency,
onActivated = self.props.exitFullscreen,
layoutOrder = 1,
onStateChanged = function(oldState, newState)
self.setExitControlState(newState)
end
}),
CloseButton = Roact.createElement(IconButton, {
icon = Images[CLOSE_BUTTON_IMAGE_ID],
iconSize = IconSize.Medium,
iconTransparency = closeButtonTransparency,
onActivated = self.props.closeRoblox,
layoutOrder = 2,
onStateChanged = function(oldState, newState)
self.setCloseControlState(newState)
end
}),
})
end
local function renderMirrorButtons()
local iconSize = getIconSize(IconSize.Medium)
return Roact.createFragment({
Roact.createElement("ImageLabel", {
BackgroundTransparency = 1,
Size = UDim2.fromOffset(iconSize, iconSize),
}),
Roact.createElement("ImageLabel", {
BackgroundTransparency = 1,
Size = UDim2.fromOffset(iconSize, iconSize),
}),
})
end
return Roact.createElement("Frame", {
BackgroundTransparency = 1,
BorderSizePixel = 0,
Position = self.titleBarPosition,
Size = UDim2.new(1, 0, 0, TITLE_BAR_HEIGHT + SHADOW_HEIGHT),
}, {
BarFrame = Roact.createElement("Frame", {
BackgroundTransparency = 1,
BorderSizePixel = 0,
Position = UDim2.fromOffset(0, 0),
Size = UDim2.new(1, 0, 0, TITLE_BAR_HEIGHT),
[Roact.Event.MouseLeave] = self.props.onDisappear,
}, {
ThreeSectionBar = Roact.createElement(ThreeSectionBar, {
BackgroundColor3 = backgroundStyle.Color,
BackgroundTransparency = titleBarTransparency,
barHeight = TITLE_BAR_HEIGHT,
marginLeft = MARGIN,
contentPaddingLeft = UDim.new(0, PADDING_BETWEEN),
renderLeft = renderMirrorButtons,
renderCenter = renderCenterText,
marginRight = MARGIN,
contentPaddingRight = UDim.new(0, PADDING_BETWEEN),
renderRight = renderRightButtons,
}),
}),
ShadowFrame = Roact.createElement("Frame", {
BackgroundTransparency = backgroundStyle.Transparency,
BackgroundColor3 = backgroundStyle.Color,
BorderSizePixel = 0,
Position = UDim2.fromOffset(0, TITLE_BAR_HEIGHT),
Size = UDim2.new(1, 0, 0, SHADOW_HEIGHT),
}, {
UIGradient = Roact.createElement("UIGradient", {
Rotation = 90,
Color = ColorSequence.new({
ColorSequenceKeypoint.new(0, Color3.new(0, 0, 0)),
ColorSequenceKeypoint.new(1, Color3.new(0, 0, 0)),
}),
Transparency = NumberSequence.new({
NumberSequenceKeypoint.new(0, 1 - GRADIENT_OPACITY),
NumberSequenceKeypoint.new(1, 1.0),
}),
})
})
})
end)
end
function FullscreenTitleBar:didMount()
self.progressMotor:start()
end
function FullscreenTitleBar:didUpdate(prevProps, prevState)
if prevProps.isTriggered ~= self.props.isTriggered then
local newProgress = self.props.isTriggered and 1 or 0
self.progressMotor:setGoal(Otter.spring(newProgress, MOTOR_OPTIONS))
end
end
function FullscreenTitleBar:willUnmount()
self.progressMotor:destroy()
end
return FullscreenTitleBar
@@ -0,0 +1,33 @@
return function()
local Packages = script.Parent.Parent.Parent.Parent
local Roact = require(Packages.Roact)
local mockStyleComponent = require(Packages.UIBlox.Utility.mockStyleComponent)
local FullscreenTitleBar = require(script.Parent.FullscreenTitleBar)
describe("lifecycle", function()
it("should mount and unmount without issue", function()
local element = mockStyleComponent({
TestTitleBar = Roact.createElement(FullscreenTitleBar, {
title = "",
})
})
local instance = Roact.mount(element, nil, "FullscreenTitleBar")
Roact.unmount(instance)
end)
it("should mount and unmount without issue with valid properties", function()
local element = mockStyleComponent({
TestTitleBar = Roact.createElement(FullscreenTitleBar, {
title = "",
onDisappear = function() end,
isTriggered = false,
exitFullscreen = function() end,
closeRoblox = function() end,
})
})
local instance = Roact.mount(element, nil, "FullscreenTitleBar")
Roact.unmount(instance)
end)
end)
end
@@ -0,0 +1,123 @@
local Bar = script.Parent
local App = Bar.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local t = require(Packages.t)
local Images = require(App.ImageSet.Images)
local IconSize = require(App.ImageSet.Enum.IconSize)
local getPageMargin = require(App.Container.getPageMargin)
local withStyle = require(UIBlox.Core.Style.withStyle)
local IconButton = require(UIBlox.App.Button.IconButton)
local GenericTextLabel = require(UIBlox.Core.Text.GenericTextLabel.GenericTextLabel)
local GetTextSize = require(UIBlox.Core.Text.GetTextSize)
local ThreeSectionBar = require(UIBlox.Core.Bar.ThreeSectionBar)
local HeaderBar = Roact.PureComponent:extend("HeaderBar")
HeaderBar.renderLeft = {
backButton = function(onActivated)
return function(_)
return Roact.createElement(IconButton, {
size = UDim2.fromOffset(0, 0),
iconSize = IconSize.Medium,
icon = Images["icons/navigation/pushBack"],
onActivated = onActivated,
})
end
end,
}
HeaderBar.validateProps = t.strictInterface({
-- The title of the screen
title = t.string,
-- The function that is called when the back button is clicked
onBack = t.optional(t.callback),
-- How tall the bar is
barHeight = t.optional(t.number),
-- How much spacing between elements to allow on the right side of the bar
contentPaddingRight = t.optional(t.UDim),
-- A function that returns a Roact Component, used for customizing buttons on the right side of the bar
renderRight = t.optional(t.callback),
renderLeft = t.optional(t.callback),
})
-- default values are taken from Abstract
HeaderBar.defaultProps = {
barHeight = 48,
contentPaddingRight = UDim.new(0, 12),
renderRight = function()
return nil
end,
renderLeft = function()
return nil
end
}
function HeaderBar:init()
self.state = {
margin = 0
}
self.onResize = function(rbx)
local margin = getPageMargin(rbx.AbsoluteSize.X)
self:setState({
margin = margin
})
end
end
function HeaderBar:render()
return withStyle(function(style)
local theme = style.Theme
local font = style.Font
local centerTextFontStyle = font.Header1
local centerTextSize = centerTextFontStyle.RelativeSize * font.BaseSize
local estimatedCenterWidth = GetTextSize(
self.props.title,
centerTextSize,
centerTextFontStyle.Font,
Vector2.new(1000, 1000)
).X
return Roact.createElement("Frame", {
BackgroundTransparency = 1,
Size = UDim2.new(1, 0, 0, self.props.barHeight),
[Roact.Change.AbsoluteSize] = self.onResize,
}, {
ThreeSectionBar = Roact.createElement(ThreeSectionBar, {
BackgroundTransparency = theme.BackgroundDefault.Transparency,
BackgroundColor3 = theme.BackgroundDefault.Color,
barHeight = self.props.barHeight,
marginLeft = self.state.margin,
renderLeft = self.props.renderLeft,
renderCenter = function()
return Roact.createElement(GenericTextLabel, {
ClipsDescendants = true,
Size = UDim2.new(1, 0, 0, centerTextSize),
Text = self.props.title,
TextTruncate = Enum.TextTruncate.AtEnd,
TextWrapped = false,
fontStyle = centerTextFontStyle,
colorStyle = theme.TextEmphasis,
})
end,
estimatedCenterWidth = estimatedCenterWidth,
marginRight = self.state.margin,
contentPaddingRight = self.props.contentPaddingRight,
renderRight = self.props.renderRight,
})
})
end)
end
return HeaderBar
@@ -0,0 +1,114 @@
return function()
local Bar = script.Parent
local App = Bar.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local mockStyleComponent = require(UIBlox.Utility.mockStyleComponent)
local HeaderBar = require(UIBlox.App.Bar.HeaderBar)
local BARSIZE_SMALL = UDim2.new(0, 320, 0, 40)
local BARSIZE_MEDIUM = UDim2.new(0, 480, 0, 40)
local BARSIZE_LARGE = UDim2.new(0, 600, 0, 40)
local MARGIN_SMALL = 12
local MARGIN_MEDIUM = 24
local MARGIN_LARGE = 48
describe("lifecycle", function()
it("should mount and unmount without issues", function()
local element = mockStyleComponent({
bar = Roact.createElement(HeaderBar, {
title = "Header Bar",
onBack = function() end,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
end)
describe("margin logic", function()
it("should have margin of 12 on small screens", function()
local frame = Instance.new("Frame")
local element = mockStyleComponent({
barFrame = Roact.createElement("Frame", {
Size = UDim2.new(0, 0, 0, 0),
}, {
bar = Roact.createElement(HeaderBar, {
title = "Header Bar",
onBack = function() end,
}),
})
})
local instance = Roact.mount(element, frame, "Frame")
local barFrame = frame:FindFirstChild("barFrame", true)
local bar = barFrame:FindFirstChild("bar")
local leftFrame = bar:FindFirstChild("leftFrame", true)
local margin = leftFrame:FindFirstChild("$margin", true)
expect(margin).to.be.ok()
barFrame.Size = BARSIZE_SMALL
local _ = bar.AbsoluteSize -- need to reference AbsoluteSize to trigger [Roact.Change.AbsoluteSize]
expect(margin.PaddingLeft.Offset).to.equal(MARGIN_SMALL)
Roact.unmount(instance)
end)
it("should have margin of 24 on medium screens", function()
local frame = Instance.new("Frame")
local element = mockStyleComponent({
barFrame = Roact.createElement("Frame", {
Size = UDim2.new(0, 0, 0, 0),
}, {
bar = Roact.createElement(HeaderBar, {
title = "Header Bar",
onBack = function() end,
}),
})
})
local instance = Roact.mount(element, frame, "Frame")
local barFrame = frame:FindFirstChild("barFrame", true)
local bar = barFrame:FindFirstChild("bar")
local leftFrame = bar:FindFirstChild("leftFrame", true)
local margin = leftFrame:FindFirstChild("$margin", true)
expect(margin).to.be.ok()
barFrame.Size = BARSIZE_MEDIUM
local _ = bar.AbsoluteSize -- need to reference AbsoluteSize to trigger [Roact.Change.AbsoluteSize]
expect(margin.PaddingLeft.Offset).to.equal(MARGIN_MEDIUM)
Roact.unmount(instance)
end)
it("should have margin of 48 on large screens", function()
local frame = Instance.new("Frame")
local element = mockStyleComponent({
barFrame = Roact.createElement("Frame", {
Size = UDim2.new(0, 0, 0, 0),
}, {
bar = Roact.createElement(HeaderBar, {
title = "Header Bar",
onBack = function() end,
}),
})
})
local instance = Roact.mount(element, frame, "Frame")
local barFrame = frame:FindFirstChild("barFrame", true)
local bar = barFrame:FindFirstChild("bar")
local leftFrame = bar:FindFirstChild("leftFrame", true)
local margin = leftFrame:FindFirstChild("$margin", true)
expect(margin).to.be.ok()
barFrame.Size = BARSIZE_LARGE
local _ = bar.AbsoluteSize -- need to reference AbsoluteSize to trigger [Roact.Change.AbsoluteSize]
expect(margin.PaddingLeft.Offset).to.equal(MARGIN_LARGE)
Roact.unmount(instance)
end)
end)
end
@@ -0,0 +1,86 @@
local Bar = script.Parent
local App = Bar.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local t = require(Packages.t)
local withStyle = require(UIBlox.Core.Style.withStyle)
local getPageMargin = require(App.Container.getPageMargin)
local GenericTextLabel = require(UIBlox.Core.Text.GenericTextLabel.GenericTextLabel)
local ThreeSectionBar = require(UIBlox.Core.Bar.ThreeSectionBar)
local RootHeaderBar = Roact.PureComponent:extend("HeaderBar")
RootHeaderBar.validateProps = t.strictInterface({
-- The title of the screen
title = t.string,
-- How tall the bar is
barHeight = t.optional(t.number),
-- A function that returns a Roact Component, used for customizing buttons on the right side of the bar
renderRight = t.optional(t.callback),
backgroundTransparency = t.optional(t.number),
})
RootHeaderBar.defaultProps = {
barHeight = 64,
renderRight = function()
return nil
end,
}
function RootHeaderBar:init()
self:setState({
margin = 0,
})
self.setPageMargin = function(rbx)
local margin = getPageMargin(rbx.AbsoluteSize.X)
self:setState({
margin = margin
})
end
end
function RootHeaderBar:render()
return withStyle(function(style)
local theme = style.Theme
local font = style.Font
return Roact.createElement("Frame", {
BackgroundTransparency = 1,
Size = UDim2.new(1, 0, 0, self.props.barHeight),
[Roact.Change.AbsoluteSize] = self.setPageMargin,
}, {
ThreeSectionBar = Roact.createElement(ThreeSectionBar, {
BackgroundTransparency = self.props.backgroundTransparency or theme.BackgroundDefault.Transparency,
BackgroundColor3 = theme.BackgroundDefault.Color,
barHeight = self.props.barHeight,
contentPaddingRight = UDim.new(0, 0),
marginLeft = self.state.margin,
marginRight = self.state.margin,
renderRight = self.props.renderRight,
renderLeft = function(props)
return Roact.createFragment({
Text = Roact.createElement(GenericTextLabel, {
fluidSizing = true,
Text = self.props.title,
TextTruncate = Enum.TextTruncate.AtEnd,
TextXAlignment = Enum.TextXAlignment.Left,
fontStyle = font.Title,
colorStyle = theme.TextEmphasis,
}, props[Roact.Children])
})
end,
})
})
end)
end
return RootHeaderBar
@@ -0,0 +1,110 @@
return function()
local Bar = script.Parent
local App = Bar.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local mockStyleComponent = require(UIBlox.Utility.mockStyleComponent)
local RootHeaderBar = require(UIBlox.App.Bar.RootHeaderBar)
local BARSIZE_SMALL = UDim2.new(0, 320, 0, 40)
local BARSIZE_MEDIUM = UDim2.new(0, 480, 0, 40)
local BARSIZE_LARGE = UDim2.new(0, 600, 0, 40)
local MARGIN_SMALL = 12
local MARGIN_MEDIUM = 24
local MARGIN_LARGE = 48
describe("lifecycle", function()
it("should mount and unmount without issues", function()
local element = mockStyleComponent({
bar = Roact.createElement(RootHeaderBar, {
title = "Root Header Bar",
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
end)
describe("margin logic", function()
it("should have margin of 12 on small screens", function()
local frame = Instance.new("Frame")
local element = mockStyleComponent({
barFrame = Roact.createElement("Frame", {
Size = UDim2.new(0, 0, 0, 0),
}, {
bar = Roact.createElement(RootHeaderBar, {
title = "Root Header Bar",
}),
})
})
local instance = Roact.mount(element, frame, "Frame")
local barFrame = frame:FindFirstChild("barFrame", true)
local bar = barFrame:FindFirstChild("bar")
local leftFrame = bar:FindFirstChild("leftFrame", true)
local margin = leftFrame:FindFirstChild("$margin", true)
expect(margin).to.be.ok()
barFrame.Size = BARSIZE_SMALL
local _ = bar.AbsoluteSize -- need to reference AbsoluteSize to trigger [Roact.Change.AbsoluteSize]
expect(margin.PaddingLeft.Offset).to.equal(MARGIN_SMALL)
Roact.unmount(instance)
end)
it("should have margin of 24 on medium screens", function()
local frame = Instance.new("Frame")
local element = mockStyleComponent({
barFrame = Roact.createElement("Frame", {
Size = UDim2.new(0, 0, 0, 0),
}, {
bar = Roact.createElement(RootHeaderBar, {
title = "Root Header Bar",
}),
})
})
local instance = Roact.mount(element, frame, "Frame")
local barFrame = frame:FindFirstChild("barFrame", true)
local bar = barFrame:FindFirstChild("bar")
local leftFrame = bar:FindFirstChild("leftFrame", true)
local margin = leftFrame:FindFirstChild("$margin", true)
expect(margin).to.be.ok()
barFrame.Size = BARSIZE_MEDIUM
local _ = bar.AbsoluteSize -- need to reference AbsoluteSize to trigger [Roact.Change.AbsoluteSize]
expect(margin.PaddingLeft.Offset).to.equal(MARGIN_MEDIUM)
Roact.unmount(instance)
end)
it("should have margin of 48 on large screens", function()
local frame = Instance.new("Frame")
local element = mockStyleComponent({
barFrame = Roact.createElement("Frame", {
Size = UDim2.new(0, 0, 0, 0),
}, {
bar = Roact.createElement(RootHeaderBar, {
title = "Root Header Bar",
}),
})
})
local instance = Roact.mount(element, frame, "Frame")
local barFrame = frame:FindFirstChild("barFrame", true)
local bar = barFrame:FindFirstChild("bar")
local leftFrame = bar:FindFirstChild("leftFrame", true)
local margin = leftFrame:FindFirstChild("$margin", true)
expect(margin).to.be.ok()
barFrame.Size = BARSIZE_LARGE
local _ = bar.AbsoluteSize -- need to reference AbsoluteSize to trigger [Roact.Change.AbsoluteSize]
expect(margin.PaddingLeft.Offset).to.equal(MARGIN_LARGE)
Roact.unmount(instance)
end)
end)
end
@@ -0,0 +1,110 @@
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local StoryView = require(ReplicatedStorage.Packages.StoryComponents.StoryView)
local StoryItem = require(ReplicatedStorage.Packages.StoryComponents.StoryItem)
local Bar = script.Parent.Parent
local App = Bar.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local FullscreenTitleBar = require(script.Parent.Parent.FullscreenTitleBar)
local DISAPPEAR_DELAY = 0.5
local TitleBarStory = Roact.PureComponent:extend("TitleBarStory")
function TitleBarStory:init()
self:setState({
isTriggered = false,
})
self.triggerTitleBar = function()
print("Mouse entering trigger area")
if not self.state.isTriggered then
self:setState({
isTriggered = true,
})
end
end
self.hideTitleBar = function()
print("Mouse leaving Title Bar area")
if self.state.isTriggered then
delay(DISAPPEAR_DELAY, function()
self:setState({
isTriggered = false,
})
end)
end
end
self.buttonControl = function()
self:setState(function(prevState)
return {
isTriggered = not prevState.isTriggered,
}
end)
end
end
function TitleBarStory:render()
return Roact.createElement("Frame", {
Size = UDim2.new(1, 0, 1, 0),
BackgroundTransparency = 1,
}, {
Layout = Roact.createElement("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
FillDirection = Enum.FillDirection.Vertical,
}),
ControlsFrame = Roact.createElement("Frame", {
Size = UDim2.new(1, 0, 0, 50),
LayoutOrder = 1,
}, {
TestButton = Roact.createElement("TextButton", {
Text = self.state.isTriggered and "Dismiss" or "Activate",
Size = UDim2.fromOffset(200, 50),
[Roact.Event.Activated] = self.buttonControl,
}),
}),
StoryItem = Roact.createElement(StoryItem, {
size = UDim2.fromScale(1, 1),
title = "FullscreenTitleBar",
subTitle = "App.Bar.FullscreenTitleBar",
layoutOrder = 2,
showDivider = true,
}, {
Demo = Roact.createElement("Frame", {
Size = UDim2.new(1, 0, 1, 0),
}, {
TriggerArea = Roact.createElement("Frame", {
BackgroundColor3 = Color3.fromRGB(0, 255, 255),
BorderSizePixel = 0,
Size = UDim2.new(1, 0, 0, 1),
[Roact.Event.MouseEnter] = self.triggerTitleBar,
}),
TitleBar = Roact.createElement(FullscreenTitleBar, {
title = "Roblox",
isTriggered = self.state.isTriggered,
onDisappear = self.hideTitleBar,
exitFullscreen = function()
print "Exit Fullscreen"
end,
closeRoblox = function()
print "Close Roblox"
end,
}),
})
}),
})
end
return function(target)
local handle = Roact.mount(Roact.createElement(StoryView, {}, {
Story = Roact.createElement(TitleBarStory),
}), target, "FullscreenTitleBar")
return function()
Roact.unmount(handle)
end
end
@@ -0,0 +1,136 @@
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local StoryView = require(ReplicatedStorage.Packages.StoryComponents.StoryView)
local StoryItem = require(ReplicatedStorage.Packages.StoryComponents.StoryItem)
local Bar = script.Parent.Parent
local App = Bar.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local Images = require(App.ImageSet.Images)
local IconSize = require(App.ImageSet.Enum.IconSize)
local HeaderBar = require(Bar.HeaderBar)
local IconButton = require(UIBlox.App.Button.IconButton)
local TextButton = require(UIBlox.App.Button.TextButton)
local function BarDemo()
return Roact.createElement(HeaderBar, {
title = string.rep("Header Bar Story", 1),
renderLeft = HeaderBar.renderLeft.backButton(function()
print("navProps.navigation.pop()")
end),
renderRight = function()
return Roact.createFragment({
search = Roact.createElement(IconButton, {
size = UDim2.fromOffset(0, 0),
iconSize = IconSize.Medium,
icon = Images["icons/common/search"],
onActivated = function()
print "Opening Search!"
end,
layoutOrder = 1,
}),
premium = Roact.createElement(IconButton, {
size = UDim2.fromOffset(0, 0),
iconSize = IconSize.Medium,
icon = Images["icons/common/goldrobux"],
onActivated = function()
print "Oooh Shiny!"
end,
layoutOrder = 2,
}),
alert = Roact.createElement(IconButton, {
size = UDim2.fromOffset(0, 0),
iconSize = IconSize.Medium,
icon = Images['icons/common/notificationOn'],
onActivated = function()
print "Alert!"
end,
layoutOrder = 3,
}),
})
end,
})
end
local function BarWithTextButtonsDemo()
return Roact.createElement(HeaderBar, {
title = string.rep("Header Bar Story", 1),
renderLeft = function()
return Roact.createFragment({
search = Roact.createElement(TextButton, {
text = "Action 1",
onActivated = function()
print "Opening Search!"
end,
layoutOrder = 1,
}),
})
end,
renderRight = function()
return Roact.createFragment({
search = Roact.createElement(TextButton, {
text = "Action 2",
onActivated = function()
print "Opening Search!"
end,
layoutOrder = 1,
}),
})
end,
})
end
return function(target)
local handle = Roact.mount(Roact.createElement(StoryView, {}, {
Story = Roact.createElement(StoryItem, {
size = UDim2.fromScale(1, 1),
title = "HeaderBar",
subTitle = "App.Bar.HeaderBar",
}, {
layout = Roact.createElement("UIListLayout"),
frame = Roact.createElement("Frame", {
BackgroundTransparency = 1,
Size = UDim2.fromOffset(600, 45),
}, {
headerBar = Roact.createElement(BarDemo)
}),
frame2 = Roact.createElement("Frame", {
BackgroundTransparency = 1,
Size = UDim2.fromOffset(361, 45),
}, {
headerBar = Roact.createElement(BarDemo)
}),
frame3 = Roact.createElement("Frame", {
BackgroundTransparency = 1,
Size = UDim2.fromOffset(300, 45),
}, {
headerBar = Roact.createElement(BarDemo)
}),
frame4 = Roact.createElement("Frame", {
BackgroundTransparency = 1,
Size = UDim2.fromOffset(600, 45),
}, {
headerBar = Roact.createElement(BarWithTextButtonsDemo)
}),
frame5 = Roact.createElement("Frame", {
BackgroundTransparency = 1,
Size = UDim2.fromOffset(361, 45),
}, {
headerBar = Roact.createElement(BarWithTextButtonsDemo)
}),
frame6 = Roact.createElement("Frame", {
BackgroundTransparency = 1,
Size = UDim2.fromOffset(300, 45),
}, {
headerBar = Roact.createElement(BarWithTextButtonsDemo)
}),
}),
}), target, "HeaderBar")
return function()
Roact.unmount(handle)
end
end
@@ -0,0 +1,61 @@
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local StoryView = require(ReplicatedStorage.Packages.StoryComponents.StoryView)
local StoryItem = require(ReplicatedStorage.Packages.StoryComponents.StoryItem)
local Bar = script.Parent.Parent
local App = Bar.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local getIconSizeUDim2 = require(App.ImageSet.getIconSizeUDim2)
local IconSize = require(App.ImageSet.Enum.IconSize)
local Images = require(App.ImageSet.Images)
local RootHeaderBar = require(Bar.RootHeaderBar)
local IconButton = require(UIBlox.App.Button.IconButton)
return function(target)
local handle = Roact.mount(Roact.createElement(StoryView, {}, {
Story = Roact.createElement(StoryItem, {
size = UDim2.new(1, 0, 1, 0),
title = "RootHeaderBar",
subTitle = "App.Bar.RootHeaderBar",
}, {
Roact.createElement(RootHeaderBar, {
title = "Root Header Bar Story Long Text Example To Test Truncation",
renderRight = function()
return Roact.createFragment({
search = Roact.createElement(IconButton, {
size = getIconSizeUDim2(IconSize.Small),
icon = Images['icons/common/search_small'],
onActivated = function()
print "Opening Search!"
end,
layoutOrder = 1,
}),
premium = Roact.createElement(IconButton, {
size = getIconSizeUDim2(IconSize.Small),
icon = Images['icons/common/goldrobux_small'],
onActivated = function()
print "Oooh Shiny!"
end,
layoutOrder = 2,
}),
alert = Roact.createElement(IconButton, {
size = getIconSizeUDim2(IconSize.Small),
icon = Images['icons/common/notificationOn'],
onActivated = function()
print "Alert!"
end,
layoutOrder = 3,
}),
})
end,
}),
})
}), target, "HeaderBar")
return function()
Roact.unmount(handle)
end
end
@@ -0,0 +1,55 @@
local Button = script.Parent
local App = Button.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local Images = require(App.ImageSet.Images)
local CursorKind = require(App.SelectionImage.CursorKind)
local withSelectionCursorProvider = require(App.SelectionImage.withSelectionCursorProvider)
local validateButtonProps = require(Button.validateButtonProps)
local GenericButton = require(UIBlox.Core.Button.GenericButton)
local ControlState = require(UIBlox.Core.Control.Enum.ControlState)
local AlertButton = Roact.PureComponent:extend("AlertButton")
local BUTTON_STATE_COLOR = {
[ControlState.Default] = "Alert",
}
local CONTENT_STATE_COLOR = {
[ControlState.Default] = "Alert",
}
AlertButton.defaultProps = {
isDisabled = false,
isLoading = false,
}
function AlertButton:render()
assert(validateButtonProps(self.props))
local image = Images["component_assets/circle_17_stroke_1"]
return withSelectionCursorProvider(function(getSelectionCursor)
return Roact.createElement(GenericButton, {
Size = self.props.size,
AnchorPoint = self.props.anchorPoint,
Position = self.props.position,
LayoutOrder = self.props.layoutOrder,
SelectionImageObject = getSelectionCursor(CursorKind.RoundedRectNoInset),
icon = self.props.icon,
text = self.props.text,
isDisabled = self.props.isDisabled,
isLoading = self.props.isLoading,
onActivated = self.props.onActivated,
onStateChanged = self.props.onStateChanged,
userInteractionEnabled = self.props.userInteractionEnabled,
buttonImage = image,
buttonStateColorMap = BUTTON_STATE_COLOR,
contentStateColorMap = CONTENT_STATE_COLOR,
})
end)
end
return AlertButton
@@ -0,0 +1,62 @@
return function()
local Button = script.Parent
local App = Button.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local Images = require(App.ImageSet.Images)
local icon = Images["icons/common/robux_small"]
local mockStyleComponent = require(UIBlox.Utility.mockStyleComponent)
local AlertButton = require(Button.AlertButton)
it("should create and destroy Alert Button with text without errors", function()
local element = mockStyleComponent({
button = Roact.createElement(AlertButton, {
text = "Button",
onActivated = function()end,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
it("should create and destroy Alert Button with text icon only without errors", function()
local element = mockStyleComponent({
button = Roact.createElement(AlertButton, {
icon = icon,
onActivated = function()end,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
it("should create and destroy Alert Button with text and text without errors", function()
local element = mockStyleComponent({
button = Roact.createElement(AlertButton, {
text = "Button",
icon = icon,
onActivated = function()end,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
it("should create and destroy a blank Alert Button without errors", function()
local element = mockStyleComponent({
button = Roact.createElement(AlertButton, {
onActivated = function()end,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
end
@@ -0,0 +1,149 @@
local ButtonRoot = script.Parent
local AppRoot = ButtonRoot.Parent
local UIBlox = AppRoot.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local Cryo = require(Packages.Cryo)
local RoactGamepad = require(Packages.RoactGamepad)
local AlertButton = require(ButtonRoot.AlertButton)
local PrimaryContextualButton = require(ButtonRoot.PrimaryContextualButton)
local PrimarySystemButton = require(ButtonRoot.PrimarySystemButton)
local SecondaryButton = require(ButtonRoot.SecondaryButton)
local GetTextSize = require(UIBlox.Core.Text.GetTextSize)
local withStyle = require(UIBlox.Core.Style.withStyle)
local FitFrame = require(Packages.FitFrame)
local FitFrameOnAxis = FitFrame.FitFrameOnAxis
local ButtonType = require(ButtonRoot.Enum.ButtonType)
local validateButtonStack = require(AppRoot.Button.Validator.validateButtonStack)
local UIBloxConfig = require(UIBlox.UIBloxConfig)
local BUTTON_HEIGHT = 36
local ButtonStack = Roact.PureComponent:extend("ButtonStack")
ButtonStack.defaultProps = {
buttonHeight = BUTTON_HEIGHT,
marginBetween = 12,
minHorizontalButtonPadding = 8,
}
function ButtonStack:init()
self.buttonRefs = RoactGamepad.createRefCache()
self.state = {
frameWidth = 0
}
self.updateFrameSize = function(rbx)
local frameWidth = rbx.AbsoluteSize.X
if frameWidth ~= self.state.frameWidth then
self:setState({
frameWidth = frameWidth,
})
end
end
end
function ButtonStack:render()
assert(validateButtonStack(self.props))
return withStyle(function(stylePalette)
local font = stylePalette.Font
local textSize = font.Body.RelativeSize * font.BaseSize
local buttons = self.props.buttons
local paddingBetween = #buttons > 1 and self.props.marginBetween or 0
local nonStackedButtonWidth = (self.state.frameWidth / #buttons) - (paddingBetween * (#buttons - 1) / #buttons)
local isButtonStacked = false
local fillDirection
if self.props.forcedFillDirection then
isButtonStacked = self.props.forcedFillDirection == Enum.FillDirection.Vertical
fillDirection = self.props.forcedFillDirection
else
for _, button in ipairs(buttons) do
local buttonTextWidth = GetTextSize(
button.props.text or "",
textSize,
font.Body.Font,
Vector2.new(self.state.frameWidth, self.props.buttonHeight)
)
if buttonTextWidth.X > (nonStackedButtonWidth - (2 * self.props.minHorizontalButtonPadding)) then
isButtonStacked = true
break
end
end
fillDirection = isButtonStacked and Enum.FillDirection.Vertical or Enum.FillDirection.Horizontal
end
local buttonSize = isButtonStacked and UDim2.new(1, 0, 0, self.props.buttonHeight)
or UDim2.new(0, nonStackedButtonWidth, 0, self.props.buttonHeight)
local buttonTable = {}
for colIndex, button in ipairs(buttons) do
local newProps = {
layoutOrder = isButtonStacked and (#buttons - colIndex) or colIndex,
size = buttonSize,
}
local buttonProps = Cryo.Dictionary.join(newProps, button.props)
local buttonComponent
if button.buttonType == ButtonType.PrimaryContextual then
buttonComponent = PrimaryContextualButton
elseif button.buttonType == ButtonType.PrimarySystem then
buttonComponent = PrimarySystemButton
elseif button.buttonType == ButtonType.Alert then
buttonComponent = AlertButton
else
buttonComponent = SecondaryButton
end
if UIBloxConfig.enableExperimentalGamepadSupport then
local gamepadFrameProps = {
Size = buttonSize,
BackgroundTransparency = 1,
[Roact.Ref] = self.buttonRefs[colIndex],
NextSelectionUp = (isButtonStacked and colIndex > 1) and self.buttonRefs[colIndex - 1] or nil,
NextSelectionDown = (isButtonStacked and colIndex < #buttons) and self.buttonRefs[colIndex + 1] or nil,
NextSelectionLeft = (not isButtonStacked and colIndex > 1) and self.buttonRefs[colIndex - 1] or nil,
NextSelectionRight = (not isButtonStacked and colIndex < #buttons) and self.buttonRefs[colIndex + 1] or nil,
inputBindings = {
Activated = RoactGamepad.Input.onBegin(Enum.KeyCode.ButtonA, button.props.onActivated),
},
}
table.insert(buttonTable, Roact.createElement(RoactGamepad.Focusable.Frame, gamepadFrameProps, {
Roact.createElement(buttonComponent, buttonProps)
}))
else
table.insert(buttonTable, Roact.createElement(buttonComponent, buttonProps))
end
end
return Roact.createElement(UIBloxConfig.enableExperimentalGamepadSupport and
RoactGamepad.Focusable[FitFrameOnAxis] or FitFrameOnAxis, {
BackgroundTransparency = 1,
contentPadding = UDim.new(0, paddingBetween),
FillDirection = fillDirection,
HorizontalAlignment = Enum.HorizontalAlignment.Center,
LayoutOrder = 3,
minimumSize = UDim2.new(1, 0, 0, self.props.buttonHeight),
[Roact.Ref] = self.props[Roact.Ref],
[Roact.Change.AbsoluteSize] = self.updateFrameSize,
NextSelectionLeft = self.props.NextSelectionLeft,
NextSelectionRight = self.props.NextSelectionRight,
NextSelectionUp = self.props.NextSelectionUp,
NextSelectionDown = self.props.NextSelectionDown,
},
buttonTable
)
end)
end
return ButtonStack
@@ -0,0 +1,33 @@
local ButtonRoot = script.Parent
local AppRoot = ButtonRoot.Parent
local UIBlox = AppRoot.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local mockStyleComponent = require(UIBlox.Utility.mockStyleComponent)
local ButtonStack = require(script.Parent.ButtonStack)
local DEFAULT_REQUIRED_PROPS = {
buttons = {
{
props = {
text = "test",
onActivated = function() end,
},
}
},
}
return function()
describe("lifecycle", function()
it("should mount and unmount button stacks without issue", function()
local tree = mockStyleComponent(
Roact.createElement(ButtonStack, DEFAULT_REQUIRED_PROPS)
)
local handle = Roact.mount(tree)
Roact.unmount(handle)
end)
end)
end
@@ -0,0 +1,13 @@
local ButtonRoot = script.Parent.Parent
local AppRoot = ButtonRoot.Parent
local UIBlox = AppRoot.Parent
local Packages = UIBlox.Parent
local enumerate = require(Packages.enumerate)
return enumerate("ButtonType", {
"Alert",
"PrimaryContextual",
"PrimarySystem",
"Secondary",
})
@@ -0,0 +1,153 @@
local App = script:FindFirstAncestor("App")
local UIBlox = App.Parent
local Core = UIBlox.Core
local Packages = UIBlox.Parent
local t = require(Packages.t)
local Roact = require(Packages.Roact)
local enumerate = require(Packages.enumerate)
local Interactable = require(Core.Control.Interactable)
local ControlState = require(Core.Control.Enum.ControlState)
local getContentStyle = require(Core.Button.getContentStyle)
local getIconSize = require(App.ImageSet.getIconSize)
local enumerateValidator = require(UIBlox.Utility.enumerateValidator)
local bindingValidator = require(Core.Utility.bindingValidator)
local validateImage = require(Core.ImageSet.Validator.validateImage)
local withStyle = require(Core.Style.withStyle)
local HoverButtonBackground = require(Core.Button.HoverButtonBackground)
local ImageSetComponent = require(Core.ImageSet.ImageSetComponent)
local IconSize = require(App.ImageSet.Enum.IconSize)
local IconButton = Roact.PureComponent:extend("IconButton")
IconButton.debugProps = enumerate("debugProps", {
"controlState",
})
IconButton.validateProps = t.strictInterface({
-- The state change callback for the button
onStateChanged = t.optional(t.callback),
-- Is the button visually disabled
isDisabled = t.optional(t.boolean),
colorStyleDefault = t.optional(t.string),
colorStyleHover = t.optional(t.string),
--A Boolean value that determines whether user events are ignored and sink input
userInteractionEnabled = t.optional(t.boolean),
-- The activated callback for the button
onActivated = t.optional(t.callback),
anchorPoint = t.optional(t.Vector2),
layoutOrder = t.optional(t.number),
position = t.optional(t.UDim2),
size = t.optional(t.UDim2),
icon = t.optional(validateImage),
iconSize = t.optional(enumerateValidator(IconSize)),
iconColor3 = t.optional(t.Color3),
iconTransparency = t.optional(t.union(t.number, bindingValidator(t.number))),
-- Override the default controlState
[IconButton.debugProps.controlState] = t.optional(enumerateValidator(ControlState)),
})
IconButton.defaultProps = {
anchorPoint = Vector2.new(0, 0),
layoutOrder = 0,
position = UDim2.new(0, 0, 0, 0),
size = nil,
icon = "",
iconSize = IconSize.Medium,
colorStyleDefault = "SystemPrimaryDefault",
colorStyleHover = "SystemPrimaryDefault",
iconColor3 = nil,
iconTransparency = nil,
isDisabled = false,
userInteractionEnabled = true,
[IconButton.debugProps.controlState] = nil,
}
function IconButton:init()
self:setState({
controlState = ControlState.Initialize
})
self.onStateChanged = function(oldState, newState)
self:setState({
controlState = newState,
})
if self.props.onStateChanged then
self.props.onStateChanged(oldState, newState)
end
end
local iconSizeToSizeScale = {
[IconSize.Small] = 1,
[IconSize.Medium] = 2,
[IconSize.Large] = 3,
[IconSize.XLarge] = 4,
[IconSize.XXLarge] = 5,
}
self.getSize = function(iconSizeMeasurement)
if self.props.size then
return self.props.size
end
local iconSize = self.props.iconSize
local extents = iconSizeMeasurement + 4 * iconSizeToSizeScale[iconSize]
return UDim2.fromOffset(extents, extents)
end
end
function IconButton:render()
return withStyle(function(style)
local iconSizeMeasurement = getIconSize(self.props.iconSize)
local size = self.getSize(iconSizeMeasurement)
local currentState = self.props[IconButton.debugProps.controlState] or self.state.controlState
local iconStateColorMap = {
[ControlState.Default] = self.props.colorStyleDefault,
[ControlState.Hover] = self.props.colorStyleHover,
}
local iconStyle = getContentStyle(iconStateColorMap, currentState, style)
return Roact.createElement(Interactable, {
AnchorPoint = self.props.anchorPoint,
LayoutOrder = self.props.layoutOrder,
Position = self.props.position,
Size = size,
isDisabled = self.props.isDisabled,
onStateChanged = self.onStateChanged,
userInteractionEnabled = self.props.userInteractionEnabled,
BackgroundTransparency = 1,
AutoButtonColor = false,
[Roact.Event.Activated] = self.props.onActivated,
}, {
sizeConstraint = Roact.createElement("UISizeConstraint", {
MinSize = Vector2.new(iconSizeMeasurement, iconSizeMeasurement),
}),
imageLabel = Roact.createElement(ImageSetComponent.Label, {
AnchorPoint = Vector2.new(0.5, 0.5),
Position = UDim2.fromScale(0.5, 0.5),
Size = UDim2.fromOffset(iconSizeMeasurement, iconSizeMeasurement),
BackgroundTransparency = 1,
Image = self.props.icon,
ImageColor3 = self.props.iconColor3 or iconStyle.Color,
ImageTransparency = self.props.iconTransparency or iconStyle.Transparency,
}),
background = currentState == ControlState.Hover and Roact.createElement(HoverButtonBackground),
})
end)
end
return IconButton
@@ -0,0 +1,268 @@
return function()
local IconButton = require(script.Parent.IconButton)
local App = script:FindFirstAncestor("App")
local UIBlox = App.Parent
local Core = UIBlox.Core
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local mockStyleComponent = require(UIBlox.Utility.mockStyleComponent)
local ControlState = require(Core.Control.Enum.ControlState)
local IconSize = require(App.ImageSet.Enum.IconSize)
describe("props", function()
local BUTTON_NAME = "test:" .. tostring(math.random(0, 999))
local runTest = function(props)
local folder = Instance.new("Folder")
local element = mockStyleComponent({
[BUTTON_NAME] = Roact.createElement(IconButton, props),
})
local instance = Roact.mount(element, folder)
return folder, function()
Roact.unmount(instance)
folder:Destroy()
end
end
local function getImageLabel(folder)
return folder:FindFirstChild("imageLabel", true)
end
local function getGuiObjectRoot(folder)
return folder:FindFirstChild(BUTTON_NAME, true)
end
local function getIconColor3(folder)
return getImageLabel(folder).ImageColor3
end
local function getIconTransparency(folder)
return getImageLabel(folder).ImageTransparency
end
local function getGuiObjectRootAbsoluteSize(folder)
return getGuiObjectRoot(folder).AbsoluteSize
end
local function getGuiObjectRootSize(folder)
return getGuiObjectRoot(folder).Size
end
describe("iconSize", function()
it("SHOULD resize gui object root AbsoluteSize", function()
local smallFolder, smallCleanup = runTest({
iconSize = IconSize.Small,
})
local mediumFolder, mediumCleanup = runTest({
iconSize = IconSize.Medium,
})
expect(getGuiObjectRootAbsoluteSize(smallFolder)).to.never.equal(getGuiObjectRootAbsoluteSize(mediumFolder))
smallCleanup()
mediumCleanup()
end)
end)
describe("appearance override props", function()
it("SHOULD override ImageLabel.ImageColor3 with iconColor3", function()
for _ = 1, 50 do
local randomColor = BrickColor.random().Color
local folder, cleanup = runTest({
iconColor3 = randomColor,
})
expect(getIconColor3(folder)).to.equal(randomColor)
cleanup()
end
end)
it("SHOULD override ImageLabel.ImageTransparency with iconTransparency", function()
for transparency = 0, 1, 0.1 do
local folder, cleanup = runTest({
iconTransparency = transparency,
})
expect(getIconTransparency(folder)).to.be.near(transparency, 0.001)
cleanup()
end
end)
it("SHOULD override ImageLabel.ImageTransparency with iconTransparency from RoactBinding value", function()
for transparency = 0, 1, 0.1 do
local folder, cleanup = runTest({
iconTransparency = Roact.createBinding(transparency),
})
expect(getIconTransparency(folder)).to.be.near(transparency, 0.001)
cleanup()
end
end)
it("SHOULD override root guiObject.AbsoluteSize with size", function()
local testSizes = {
UDim2.fromScale(0.5, 0.5),
UDim2.fromScale(1, 1),
UDim2.fromOffset(1000, 10),
UDim2.fromOffset(0, 100),
}
for _, size in ipairs(testSizes) do
local controlGroupFolder, cleanupControlGroup = runTest({
size = nil,
})
local variableGroupFolder, cleanupVariableGroup = runTest({
size = size,
})
local controlSize = getGuiObjectRootAbsoluteSize(controlGroupFolder)
local variableSize = getGuiObjectRootAbsoluteSize(variableGroupFolder)
expect(controlSize).to.never.equal(variableSize)
expect(getGuiObjectRootSize(variableGroupFolder)).to.equal(size)
cleanupControlGroup()
cleanupVariableGroup()
end
end)
end)
describe("positional props", function()
it("SHOULD respect AnchorPoint", function()
local function testAnchorPoint(anchorPoint)
local folder, cleanup = runTest({
anchorPoint = anchorPoint,
})
local guiObject = folder:FindFirstChild(BUTTON_NAME, true)
expect(guiObject.AnchorPoint).to.equal(anchorPoint)
cleanup()
end
testAnchorPoint(Vector2.new(0, 0))
testAnchorPoint(Vector2.new(0, 1))
testAnchorPoint(Vector2.new(1, 0))
testAnchorPoint(Vector2.new(1, 1))
testAnchorPoint(Vector2.new(0.5, 0.5))
end)
it("SHOULD respect Position", function()
local function testPosition(position)
local folder, cleanup = runTest({
position = position,
})
local guiObject = folder:FindFirstChild(BUTTON_NAME, true)
expect(guiObject.Position).to.equal(position)
cleanup()
end
testPosition(UDim2.new(0, 0, 0, 0))
testPosition(UDim2.new(0.5, 10, 1, 20))
testPosition(UDim2.fromScale(1, 1))
testPosition(UDim2.fromOffset(100, 000))
end)
it("SHOULD respect LayoutOrder", function()
local function testLayoutOrder(layoutOrder)
local folder, cleanup = runTest({
layoutOrder = layoutOrder,
})
local guiObject = folder:FindFirstChild(BUTTON_NAME, true)
expect(guiObject.LayoutOrder).to.equal(layoutOrder)
cleanup()
end
testLayoutOrder(0)
testLayoutOrder(1)
testLayoutOrder(2)
end)
it("SHOULD respect Size", function()
local function testSize(size)
local folder, cleanup = runTest({
size = size,
})
local guiObject = folder:FindFirstChild(BUTTON_NAME, true)
expect(guiObject.Size).to.equal(size)
cleanup()
end
testSize(UDim2.new(0, 0, 0, 0))
testSize(UDim2.new(0.5, 10, 1, 20))
testSize(UDim2.fromScale(1, 1))
testSize(UDim2.fromOffset(100, 000))
end)
end)
describe("debugProps.controlState", function()
local function isShowingBackground(folder)
return folder:FindFirstChild("background", true) ~= nil
end
local function isImageTransparent(folder)
local imageLabel = folder:FindFirstChild("imageLabel", true)
assert(imageLabel, "imageLabel never mounted")
return imageLabel.ImageTransparency > 0
end
it("SHOULD render ControlState.Default with no issues", function()
local folder, cleanup = runTest({
[IconButton.debugProps.controlState] = ControlState.Default,
})
expect(isShowingBackground(folder)).to.equal(false)
expect(isImageTransparent(folder)).to.equal(false)
cleanup()
end)
it("SHOULD render ControlState.Hover with no issues", function()
local folder, cleanup = runTest({
[IconButton.debugProps.controlState] = ControlState.Hover,
})
expect(isShowingBackground(folder)).to.equal(true)
expect(isImageTransparent(folder)).to.equal(false)
cleanup()
end)
it("SHOULD render ControlState.Pressed with no issues", function()
local folder, cleanup = runTest({
[IconButton.debugProps.controlState] = ControlState.Pressed,
})
expect(isShowingBackground(folder)).to.equal(false)
expect(isImageTransparent(folder)).to.equal(true)
cleanup()
end)
it("SHOULD render ControlState.Disabled with no issues", function()
local folder, cleanup = runTest({
[IconButton.debugProps.controlState] = ControlState.Disabled,
})
expect(isShowingBackground(folder)).to.equal(false)
expect(isImageTransparent(folder)).to.equal(true)
cleanup()
end)
end)
end)
end
@@ -0,0 +1,55 @@
local Button = script.Parent
local App = Button.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local Images = require(App.ImageSet.Images)
local CursorKind = require(App.SelectionImage.CursorKind)
local withSelectionCursorProvider = require(App.SelectionImage.withSelectionCursorProvider)
local validateButtonProps = require(Button.validateButtonProps)
local GenericButton = require(UIBlox.Core.Button.GenericButton)
local ControlState = require(UIBlox.Core.Control.Enum.ControlState)
local PrimaryContextualButton = Roact.PureComponent:extend("PrimaryContextualButton")
local BUTTON_STATE_COLOR = {
[ControlState.Default] = "ContextualPrimaryDefault",
[ControlState.Hover] = "ContextualPrimaryOnHover",
}
local CONTENT_STATE_COLOR = {
[ControlState.Default] = "ContextualPrimaryContent",
}
PrimaryContextualButton.defaultProps = {
isDisabled = false,
isLoading = false,
}
function PrimaryContextualButton:render()
assert(validateButtonProps(self.props))
local image = Images["component_assets/circle_17"]
return withSelectionCursorProvider(function(getSelectionCursor)
return Roact.createElement(GenericButton, {
Size = self.props.size,
AnchorPoint = self.props.anchorPoint,
Position = self.props.position,
LayoutOrder = self.props.layoutOrder,
SelectionImageObject = getSelectionCursor(CursorKind.RoundedRectNoInset),
icon = self.props.icon,
text = self.props.text,
isDisabled = self.props.isDisabled,
isLoading = self.props.isLoading,
onActivated = self.props.onActivated,
onStateChanged = self.props.onStateChanged,
userInteractionEnabled = self.props.userInteractionEnabled,
buttonImage = image,
buttonStateColorMap = BUTTON_STATE_COLOR,
contentStateColorMap = CONTENT_STATE_COLOR,
})
end)
end
return PrimaryContextualButton
@@ -0,0 +1,62 @@
return function()
local Button = script.Parent
local App = Button.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local Images = require(App.ImageSet.Images)
local icon = Images["icons/common/robux_small"]
local mockStyleComponent = require(UIBlox.Utility.mockStyleComponent)
local PrimaryContextualButton = require(Button.PrimaryContextualButton)
it("should create and destroy Primary Contextual Button with text without errors", function()
local element = mockStyleComponent({
button = Roact.createElement(PrimaryContextualButton, {
text = "Button",
onActivated = function()end,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
it("should create and destroy Primary Contextual Button with text icon only without errors", function()
local element = mockStyleComponent({
button = Roact.createElement(PrimaryContextualButton, {
icon = icon,
onActivated = function()end,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
it("should create and destroy Primary Contextual Button with text and text without errors", function()
local element = mockStyleComponent({
button = Roact.createElement(PrimaryContextualButton, {
text = "Button",
icon = icon,
onActivated = function()end,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
it("should create and destroy a blank Primary Contextual Button without errors", function()
local element = mockStyleComponent({
button = Roact.createElement(PrimaryContextualButton, {
onActivated = function()end,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
end
@@ -0,0 +1,56 @@
local Button = script.Parent
local App = Button.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local Images = require(App.ImageSet.Images)
local CursorKind = require(App.SelectionImage.CursorKind)
local withSelectionCursorProvider = require(App.SelectionImage.withSelectionCursorProvider)
local validateButtonProps = require(Button.validateButtonProps)
local GenericButton = require(UIBlox.Core.Button.GenericButton)
local ControlState = require(UIBlox.Core.Control.Enum.ControlState)
local PrimarySystemButton = Roact.PureComponent:extend("PrimarySystemButton")
local BUTTON_STATE_COLOR = {
[ControlState.Default] = "SystemPrimaryDefault",
[ControlState.Hover] = "SystemPrimaryOnHover",
}
local CONTENT_STATE_COLOR = {
[ControlState.Default] = "SystemPrimaryContent",
}
PrimarySystemButton.defaultProps = {
isDisabled = false,
isLoading = false,
}
function PrimarySystemButton:render()
assert(validateButtonProps(self.props))
local image = Images["component_assets/circle_17"]
return withSelectionCursorProvider(function(getSelectionCursor)
return Roact.createElement(GenericButton, {
Size = self.props.size,
AnchorPoint = self.props.anchorPoint,
Position = self.props.position,
LayoutOrder = self.props.layoutOrder,
SelectionImageObject = getSelectionCursor(CursorKind.RoundedRectNoInset),
icon = self.props.icon,
text = self.props.text,
isDisabled = self.props.isDisabled,
isLoading = self.props.isLoading,
onActivated = self.props.onActivated,
onStateChanged = self.props.onStateChanged,
userInteractionEnabled = self.props.userInteractionEnabled,
buttonImage = image,
buttonStateColorMap = BUTTON_STATE_COLOR,
contentStateColorMap = CONTENT_STATE_COLOR,
})
end)
end
return PrimarySystemButton
@@ -0,0 +1,62 @@
return function()
local Button = script.Parent
local App = Button.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local Images = require(App.ImageSet.Images)
local icon = Images["icons/common/robux_small"]
local mockStyleComponent = require(UIBlox.Utility.mockStyleComponent)
local PrimarySystemButton = require(Button.PrimarySystemButton)
it("should create and destroy Primary System Button with text without errors", function()
local element = mockStyleComponent({
button = Roact.createElement(PrimarySystemButton, {
text = "Button",
onActivated = function()end,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
it("should create and destroy Primary System Button with text icon only without errors", function()
local element = mockStyleComponent({
button = Roact.createElement(PrimarySystemButton, {
icon = icon,
onActivated = function()end,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
it("should create and destroy Primary System Button with text and text without errors", function()
local element = mockStyleComponent({
button = Roact.createElement(PrimarySystemButton, {
text = "Button",
icon = icon,
onActivated = function()end,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
it("should create and destroy a blank Primary System Button without errors", function()
local element = mockStyleComponent({
button = Roact.createElement(PrimarySystemButton, {
onActivated = function()end,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
end
@@ -0,0 +1,56 @@
local Button = script.Parent
local App = Button.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local Images = require(App.ImageSet.Images)
local CursorKind = require(App.SelectionImage.CursorKind)
local withSelectionCursorProvider = require(App.SelectionImage.withSelectionCursorProvider)
local validateButtonProps = require(Button.validateButtonProps)
local GenericButton = require(UIBlox.Core.Button.GenericButton)
local ControlState = require(UIBlox.Core.Control.Enum.ControlState)
local SecondaryButton = Roact.PureComponent:extend("SecondaryButton")
local BUTTON_STATE_COLOR = {
[ControlState.Default] = "SecondaryDefault",
[ControlState.Hover] = "SecondaryOnHover",
}
local CONTENT_STATE_COLOR = {
[ControlState.Default] = "SecondaryContent",
[ControlState.Hover] = "SecondaryOnHover",
}
SecondaryButton.defaultProps = {
isDisabled = false,
isLoading = false,
}
function SecondaryButton:render()
assert(validateButtonProps(self.props))
local image = Images["component_assets/circle_17_stroke_1"]
return withSelectionCursorProvider(function(getSelectionCursor)
return Roact.createElement(GenericButton, {
Size = self.props.size,
AnchorPoint = self.props.anchorPoint,
Position = self.props.position,
LayoutOrder = self.props.layoutOrder,
SelectionImageObject = getSelectionCursor(CursorKind.RoundedRectNoInset),
icon = self.props.icon,
text = self.props.text,
isDisabled = self.props.isDisabled,
isLoading = self.props.isLoading,
onActivated = self.props.onActivated,
onStateChanged = self.props.onStateChanged,
userInteractionEnabled = self.props.userInteractionEnabled,
buttonImage = image,
buttonStateColorMap = BUTTON_STATE_COLOR,
contentStateColorMap = CONTENT_STATE_COLOR,
})
end)
end
return SecondaryButton
@@ -0,0 +1,62 @@
return function()
local Button = script.Parent
local App = Button.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local Images = require(App.ImageSet.Images)
local icon = Images["icons/common/robux_small"]
local mockStyleComponent = require(UIBlox.Utility.mockStyleComponent)
local SecondaryButton = require(Button.SecondaryButton)
it("should create and destroy Secondary Button with text without errors", function()
local element = mockStyleComponent({
button = Roact.createElement(SecondaryButton, {
text = "Button",
onActivated = function()end,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
it("should create and destroy Secondary Button with text icon only without errors", function()
local element = mockStyleComponent({
button = Roact.createElement(SecondaryButton, {
icon = icon,
onActivated = function()end,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
it("should create and destroy Secondary Button with text and text without errors", function()
local element = mockStyleComponent({
button = Roact.createElement(SecondaryButton, {
text = "Button",
icon = icon,
onActivated = function()end,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
it("should create and destroy a blank Secondary Button without errors", function()
local element = mockStyleComponent({
button = Roact.createElement(SecondaryButton, {
onActivated = function()end,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
end
@@ -0,0 +1,139 @@
local App = script:FindFirstAncestor("App")
local UIBlox = App.Parent
local Core = UIBlox.Core
local Packages = UIBlox.Parent
local t = require(Packages.t)
local Roact = require(Packages.Roact)
local enumerate = require(Packages.enumerate)
local Interactable = require(Core.Control.Interactable)
local ControlState = require(Core.Control.Enum.ControlState)
local getContentStyle = require(Core.Button.getContentStyle)
local GetTextSize = require(Core.Text.GetTextSize)
local enumerateValidator = require(UIBlox.Utility.enumerateValidator)
local withStyle = require(Core.Style.withStyle)
local GenericTextLabel = require(Core.Text.GenericTextLabel.GenericTextLabel)
local HoverButtonBackground = require(Core.Button.HoverButtonBackground)
local VERTICAL_PADDING = 8
local HORIZONTAL_PADDING = 11
local TextButton = Roact.PureComponent:extend("TextButton")
TextButton.debugProps = enumerate("debugProps", {
"getTextSize",
"controlState",
})
TextButton.validateProps = t.strictInterface({
-- The state change callback for the button
onStateChanged = t.optional(t.callback),
-- Is the button visually disabled
isDisabled = t.optional(t.boolean),
fontStyle = t.optional(t.string),
colorStyleDefault = t.optional(t.string),
colorStyleHover = t.optional(t.string),
--A Boolean value that determines whether user events are ignored and sink input
userInteractionEnabled = t.optional(t.boolean),
-- The activated callback for the button
onActivated = t.optional(t.callback),
anchorPoint = t.optional(t.Vector2),
layoutOrder = t.optional(t.number),
position= t.optional(t.UDim2),
size = t.optional(t.UDim2),
text = t.optional(t.string),
-- A callback that replaces getTextSize implementation
[TextButton.debugProps.getTextSize] = t.optional(t.callback),
-- Override the default controlState
[TextButton.debugProps.controlState] = t.optional(enumerateValidator(ControlState)),
})
TextButton.defaultProps = {
anchorPoint = Vector2.new(0, 0),
layoutOrder = 0,
position = UDim2.new(0, 0, 0, 0),
size = UDim2.fromScale(0, 0),
text = "",
fontStyle = "Header2",
colorStyleDefault = "SystemPrimaryDefault",
colorStyleHover = "SystemPrimaryDefault",
isDisabled = false,
userInteractionEnabled = true,
[TextButton.debugProps.getTextSize] = GetTextSize,
[TextButton.debugProps.controlState] = nil,
}
function TextButton:init()
self:setState({
controlState = ControlState.Initialize
})
self.onStateChanged = function(oldState, newState)
self:setState({
controlState = newState,
})
if self.props.onStateChanged then
self.props.onStateChanged(oldState, newState)
end
end
end
function TextButton:render()
return withStyle(function(style)
local currentState = self.props[TextButton.debugProps.controlState] or self.state.controlState
local textStateColorMap = {
[ControlState.Default] = self.props.colorStyleDefault,
[ControlState.Hover] = self.props.colorStyleHover,
}
local textStyle = getContentStyle(textStateColorMap, currentState, style)
local fontStyle = style.Font[self.props.fontStyle]
local fontSize = fontStyle.RelativeSize * style.Font.BaseSize
local getTextSize = self.props[TextButton.debugProps.getTextSize]
local textWidth = getTextSize(self.props.text, fontSize, fontStyle.Font, Vector2.new(10000, 0)).X
return Roact.createElement(Interactable, {
AnchorPoint = self.props.anchorPoint,
LayoutOrder = self.props.layoutOrder,
Position = self.props.position,
Size = self.props.size,
isDisabled = self.props.isDisabled,
onStateChanged = self.onStateChanged,
userInteractionEnabled = self.props.userInteractionEnabled,
BackgroundTransparency = 1,
AutoButtonColor = false,
[Roact.Event.Activated] = self.props.onActivated,
}, {
sizeConstraint = Roact.createElement("UISizeConstraint", {
MinSize = Vector2.new(textWidth + VERTICAL_PADDING*2, fontSize + HORIZONTAL_PADDING*2),
}),
textLabel = Roact.createElement(GenericTextLabel, {
AnchorPoint = Vector2.new(0.5, 0.5),
Position = UDim2.fromScale(0.5, 0.5),
BackgroundTransparency = 1,
Text = self.props.text,
fontStyle = fontStyle,
colorStyle = textStyle,
}),
background = currentState == ControlState.Hover and Roact.createElement(HoverButtonBackground)
})
end)
end
return TextButton
@@ -0,0 +1,276 @@
return function()
local TextButton = require(script.Parent.TextButton)
local App = script:FindFirstAncestor("App")
local UIBlox = App.Parent
local Core = UIBlox.Core
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local mockStyleComponent = require(UIBlox.Utility.mockStyleComponent)
local ControlState = require(Core.Control.Enum.ControlState)
local noOpt = function()
end
local text = "Button"
it("should create and destroy a button without errors", function()
local folder = Instance.new("Folder")
local element = mockStyleComponent({
button = Roact.createElement(TextButton, {
text = text,
onActivated = noOpt,
fontStyle = "Body",
colorStyleDefault = "UIDefault",
colorStyleHover = "UIDefault",
}),
})
local instance = Roact.mount(element, folder)
local label = folder:FindFirstChildWhichIsA("TextLabel", true)
expect(label.Text).to.equal(text)
Roact.unmount(instance)
end)
it("should create and destroy a button that is disabled without errors", function()
local element = mockStyleComponent({
button = Roact.createElement(TextButton, {
text = text,
onActivated = noOpt,
isDisabled = true,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
it("should create and destroy a button without text without errors", function()
local element = mockStyleComponent({
button = Roact.createElement(TextButton, {
onActivated = noOpt,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
it("should be created as a disabled button", function()
local buttonState = nil
local element = mockStyleComponent({
button = Roact.createElement(TextButton, {
onActivated = noOpt,
onStateChanged = function(_, newState)
buttonState = newState
end,
isDisabled = true,
}),
})
local instance = Roact.mount(element)
expect(buttonState).to.equal(ControlState.Disabled)
Roact.unmount(instance)
end)
describe("text prop", function()
local BUTTON_NAME = "test:" .. tostring(math.random(0, 999))
local runTest = function(props)
local folder = Instance.new("Folder")
local element = mockStyleComponent({
[BUTTON_NAME] = Roact.createElement(TextButton, props),
})
local instance = Roact.mount(element, folder)
return folder, function()
Roact.unmount(instance)
folder:Destroy()
end
end
it("SHOULD resize to text when not given size property", function()
local folder1, cleanup1 = runTest({
text = string.rep("!", 1),
[TextButton.debugProps.getTextSize] = function()
return Vector2.new(1, 1)
end,
})
local folder2, cleanup2 = runTest({
text = string.rep("!", 10),
[TextButton.debugProps.getTextSize] = function()
return Vector2.new(10, 1)
end,
})
local firstSize = folder1:FindFirstChild(BUTTON_NAME, true).AbsoluteSize
local secondSize = folder2:FindFirstChild(BUTTON_NAME, true).AbsoluteSize
expect(firstSize).to.never.equal(secondSize)
cleanup1()
cleanup2()
end)
end)
describe("positional props", function()
local BUTTON_NAME = "test:" .. tostring(math.random(0, 999))
local runTest = function(props)
local folder = Instance.new("Folder")
local element = mockStyleComponent({
[BUTTON_NAME] = Roact.createElement(TextButton, props),
})
local instance = Roact.mount(element, folder)
return folder, function()
Roact.unmount(instance)
folder:Destroy()
end
end
it("SHOULD respect AnchorPoint", function()
local function testAnchorPoint(anchorPoint)
local folder, cleanup = runTest({
anchorPoint = anchorPoint,
})
local guiObject = folder:FindFirstChild(BUTTON_NAME, true)
expect(guiObject.AnchorPoint).to.equal(anchorPoint)
cleanup()
end
testAnchorPoint(Vector2.new(0, 0))
testAnchorPoint(Vector2.new(0, 1))
testAnchorPoint(Vector2.new(1, 0))
testAnchorPoint(Vector2.new(1, 1))
testAnchorPoint(Vector2.new(0.5, 0.5))
end)
it("SHOULD respect Position", function()
local function testPosition(position)
local folder, cleanup = runTest({
position = position,
})
local guiObject = folder:FindFirstChild(BUTTON_NAME, true)
expect(guiObject.Position).to.equal(position)
cleanup()
end
testPosition(UDim2.new(0, 0, 0, 0))
testPosition(UDim2.new(0.5, 10, 1, 20))
testPosition(UDim2.fromScale(1, 1))
testPosition(UDim2.fromOffset(100, 000))
end)
it("SHOULD respect LayoutOrder", function()
local function testLayoutOrder(layoutOrder)
local folder, cleanup = runTest({
layoutOrder = layoutOrder,
})
local guiObject = folder:FindFirstChild(BUTTON_NAME, true)
expect(guiObject.LayoutOrder).to.equal(layoutOrder)
cleanup()
end
testLayoutOrder(0)
testLayoutOrder(1)
testLayoutOrder(2)
end)
it("SHOULD respect Size", function()
local function testSize(size)
local folder, cleanup = runTest({
size = size,
})
local guiObject = folder:FindFirstChild(BUTTON_NAME, true)
expect(guiObject.Size).to.equal(size)
cleanup()
end
testSize(UDim2.new(0, 0, 0, 0))
testSize(UDim2.new(0.5, 10, 1, 20))
testSize(UDim2.fromScale(1, 1))
testSize(UDim2.fromOffset(100, 000))
end)
end)
describe("debugProps.controlState", function()
local BUTTON_NAME = "test:" .. tostring(math.random(0, 999))
local runTest = function(props)
local folder = Instance.new("Folder")
local element = mockStyleComponent({
[BUTTON_NAME] = Roact.createElement(TextButton, props),
})
local instance = Roact.mount(element, folder)
return folder, function()
Roact.unmount(instance)
folder:Destroy()
end
end
local function isShowingBackground(folder)
return folder:FindFirstChild("background", true) ~= nil
end
local function isTextTransparent(folder)
local textLabel = folder:FindFirstChild("textLabel", true)
assert(textLabel, "textLabel never mounted")
return textLabel.TextTransparency > 0
end
it("SHOULD render ControlState.Default with no issues", function()
local folder, cleanup = runTest({
[TextButton.debugProps.controlState] = ControlState.Default,
})
expect(isShowingBackground(folder)).to.equal(false)
expect(isTextTransparent(folder)).to.equal(false)
cleanup()
end)
it("SHOULD render ControlState.Hover with no issues", function()
local folder, cleanup = runTest({
[TextButton.debugProps.controlState] = ControlState.Hover,
})
expect(isShowingBackground(folder)).to.equal(true)
expect(isTextTransparent(folder)).to.equal(false)
cleanup()
end)
it("SHOULD render ControlState.Pressed with no issues", function()
local folder, cleanup = runTest({
[TextButton.debugProps.controlState] = ControlState.Pressed,
})
expect(isShowingBackground(folder)).to.equal(false)
expect(isTextTransparent(folder)).to.equal(true)
cleanup()
end)
it("SHOULD render ControlState.Disabled with no issues", function()
local folder, cleanup = runTest({
[TextButton.debugProps.controlState] = ControlState.Disabled,
})
expect(isShowingBackground(folder)).to.equal(false)
expect(isTextTransparent(folder)).to.equal(true)
cleanup()
end)
end)
end
@@ -0,0 +1,46 @@
local validatorRoot = script.Parent
local ButtonRoot = validatorRoot.Parent
local AppRoot = ButtonRoot.Parent
local UIBlox = AppRoot.Parent
local Packages = UIBlox.Parent
local t = require(Packages.t)
local Roact = require(Packages.Roact)
local enumerateValidator = require(UIBlox.Utility.enumerateValidator)
local validateButtonProps = require(ButtonRoot.validateButtonProps)
local ButtonType = require(ButtonRoot.Enum.ButtonType)
return t.strictInterface({
-- buttons: A table of button tables that contain props that PrimaryContextualButton,
-- AlertButton, PrimarySystemButton, or SecondaryButton allow. Also contains a prop "buttonType"
-- to determine which of these button types to use.
buttons = t.array(t.strictInterface({
buttonType = t.optional(enumerateValidator(ButtonType)),
props = validateButtonProps,
})),
buttonHeight = t.optional(t.numberMin(0)),
-- forceFillDirection: What fill direction to force into. If nil, then the fillDirection
-- will be Vertical and automatically change to Horizontal if any button's text is
-- too long.
forcedFillDirection = t.optional(t.enum(Enum.FillDirection)),
-- marginBetween: the margin between each button.
marginBetween = t.optional(t.numberMin(0)),
-- minHorizontalButtonPadding: The minimum left and right padding used to calculate
-- the when the button text overflows and automatically changes fillDirection.
-- The overflow calculation will be if the length of the button text is over
-- the button size - (2 * minHorizontalButtonPadding).
minHorizontalButtonPadding = t.optional(t.numberMin(0)),
-- optional parameters for RoactGamepad
NextSelectionLeft = t.optional(t.table),
NextSelectionRight = t.optional(t.table),
NextSelectionUp = t.optional(t.table),
NextSelectionDown = t.optional(t.table),
[Roact.Ref] = t.optional(t.table),
})
@@ -0,0 +1,41 @@
local Button = script.Parent
local App = Button.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Core = UIBlox.Core
local t = require(Packages.t)
local validateImage = require(Core.ImageSet.Validator.validateImage)
return t.strictInterface({
--The size of the button
size = t.optional(t.UDim2),
--The anchor point of the button
anchorPoint = t.optional(t.Vector2),
--The position of the button
position = t.optional(t.UDim2),
--The layout order of the button
layoutOrder = t.optional(t.number),
--The icon of the button
icon = t.optional(validateImage),
--The text of the button
text = t.optional(t.string),
--Is the button disabled
isDisabled = t.optional(t.boolean),
--Is the button loading
isLoading = t.optional(t.boolean),
--The activated callback for the button
onActivated = t.callback,
--A Boolean value that determines whether user events are ignored and sink input
userInteractionEnabled = t.optional(t.boolean),
})
@@ -0,0 +1,88 @@
local SelectionGroup = script.Parent
local Small = SelectionGroup.Parent
local Cell = Small.Parent
local App = Cell.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local t = require(Packages.t)
local Images = require(Packages.UIBlox.App.ImageSet.Images)
local GenericSelectionCell = require(Packages.UIBlox.Core.Cell.GenericSelectionCell)
local DEFAULT_IMAGE = Images["component_assets/circle_24_stroke_1"]
local SELECTED_IMAGE = Images["component_assets/circle_16"]
local DEFAULT_IMAGE_SIZE = 24
local SELECTED_IMAGE_SIZE = 16
local CELL_SIZE = 56
local SmallRadioButtonCell = Roact.PureComponent:extend("SmallRadioButtonCell")
SmallRadioButtonCell.validateProps = t.strictInterface({
-- Unique key to identify this selection.
key = t.string,
-- Text to display
text = t.optional(t.string),
-- Callback for when this selection is activated.
onActivated = t.optional(t.callback),
-- Whether this selection is selected or not.
isSelected = t.optional(t.boolean),
-- If this cell is disabled
isDisabled = t.optional(t.boolean),
-- The LayoutOrder.
layoutOrder = t.optional(t.number),
-- optional parameters for RoactGamepad
[Roact.Ref] = t.optional(t.table),
NextSelectionLeft = t.optional(t.table),
NextSelectionRight = t.optional(t.table),
NextSelectionUp = t.optional(t.table),
NextSelectionDown = t.optional(t.table),
})
SmallRadioButtonCell.defaultProps = {
text = "",
isSelected = false,
}
function SmallRadioButtonCell:init()
self.onSetValue = function()
self.props.onActivated(self.props.key)
end
end
function SmallRadioButtonCell:render()
assert(self.validateProps(self.props))
return Roact.createElement("Frame", {
Size = UDim2.new(1, 0, 0, CELL_SIZE),
BorderSizePixel = 0,
BackgroundTransparency = 1,
LayoutOrder = self.props.layoutOrder,
}, {
GenericSelectionCell = Roact.createElement(GenericSelectionCell, {
isSelected = self.props.isSelected,
isDisabled = self.props.isDisabled,
defaultImage = DEFAULT_IMAGE,
selectedImage = SELECTED_IMAGE,
defaultImageSize = DEFAULT_IMAGE_SIZE,
selectedImageSize = SELECTED_IMAGE_SIZE,
text = self.props.text,
onActivated = self.onSetValue,
[Roact.Ref] = self.props[Roact.Ref],
NextSelectionUp = self.props.NextSelectionUp,
NextSelectionDown = self.props.NextSelectionDown,
NextSelectionLeft = self.props.NextSelectionLeft,
NextSelectionRight = self.props.NextSelectionRight,
}),
})
end
return SmallRadioButtonCell
@@ -0,0 +1,25 @@
return function()
local SelectionGroup = script.Parent
local Small = SelectionGroup.Parent
local Cell = Small.Parent
local App = Cell.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local mockStyleComponent = require(UIBlox.Utility.mockStyleComponent)
local SmallRadioButtonCell = require(script.Parent.SmallRadioButtonCell)
it("should create and destroy SmallRadioButtonCell without errors", function()
local element = mockStyleComponent({
smallRadioButtonCell = Roact.createElement(SmallRadioButtonCell, {
key = "1",
})
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
end
@@ -0,0 +1,103 @@
local SelectionGroup = script.Parent
local Small = SelectionGroup.Parent
local Cell = Small.Parent
local App = Cell.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local RoactGamepad = require(Packages.RoactGamepad)
local t = require(Packages.t)
local UIBloxConfig = require(UIBlox.UIBloxConfig)
local SmallRadioButtonCell = require(UIBlox.App.Cell.Small.SelectionGroup.SmallRadioButtonCell)
local SmallRadioButtonGroup = Roact.PureComponent:extend("SmallRadioButtonGroup")
local buttonInterface = t.strictInterface({
text = t.string,
key = t.string,
isDisabled = t.optional(t.boolean),
})
SmallRadioButtonGroup.validateProps = t.strictInterface({
-- List of text, key pairs that will be used for each radio button.
items = t.optional(t.array(t.tuple(buttonInterface))),
-- Which key is currently selected.
selectedValue = t.optional(t.string),
-- Callback for when a cell is activated.
onActivated = t.callback,
-- Layout order for this component.
layoutOrder = t.optional(t.number),
-- optional parameters for RoactGamepad
NextSelectionLeft = t.optional(t.table),
NextSelectionRight = t.optional(t.table),
NextSelectionUp = t.optional(t.table),
NextSelectionDown = t.optional(t.table),
[Roact.Ref] = t.optional(t.table),
})
SmallRadioButtonGroup.defaultProps = {
selectedValue = nil,
}
function SmallRadioButtonGroup:init()
self.gamepadRefs = RoactGamepad.createRefCache()
end
function SmallRadioButtonGroup:render()
assert(self.validateProps(self.props))
local smallRadioButtonCellGroup = {}
smallRadioButtonCellGroup.layout = Roact.createElement("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
Padding = UDim.new(0, 1),
})
for index, button in ipairs(self.props.items) do
if UIBloxConfig.enableExperimentalGamepadSupport then
smallRadioButtonCellGroup["smallRadioButtonCell"..button.key] =
Roact.createElement(RoactGamepad.Focusable[SmallRadioButtonCell], {
key = button.key,
text = button.text,
onActivated = self.props.onActivated,
isSelected = self.props.selectedValue == button.key,
isDisabled = button.isDisabled,
layoutOrder = index,
[Roact.Ref] = self.gamepadRefs[index],
NextSelectionUp = index > 1 and self.gamepadRefs[index - 1] or nil,
NextSelectionDown = index < #self.props.items and self.gamepadRefs[index + 1] or nil,
})
else
smallRadioButtonCellGroup["smallRadioButtonCell"..button.key] = Roact.createElement(SmallRadioButtonCell, {
key = button.key,
text = button.text,
onActivated = self.props.onActivated,
isSelected = self.props.selectedValue == button.key,
isDisabled = button.isDisabled,
layoutOrder = index,
})
end
end
local gamepadEnabled = (UIBloxConfig.enableExperimentalGamepadSupport and self.props.items and #self.props.items > 0)
return Roact.createElement(gamepadEnabled and RoactGamepad.Focusable.Frame or "Frame", {
defaultChild = gamepadEnabled and self.gamepadRefs[1] or nil,
Size = UDim2.new(1, 0, 1, 0),
BackgroundTransparency = 1,
LayoutOrder = self.props.layoutOrder,
NextSelectionLeft = self.props.NextSelectionLeft,
NextSelectionRight = self.props.NextSelectionRight,
NextSelectionDown = self.props.NextSelectionDown,
NextSelectionUp = self.props.NextSelectionUp,
[Roact.Ref] = self.props[Roact.Ref],
}, smallRadioButtonCellGroup)
end
return SmallRadioButtonGroup
@@ -0,0 +1,47 @@
return function()
local SelectionGroup = script.Parent
local Small = SelectionGroup.Parent
local Cell = Small.Parent
local App = Cell.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local mockStyleComponent = require(UIBlox.Utility.mockStyleComponent)
local SmallRadioButtonGroup = require(script.Parent.SmallRadioButtonGroup)
local ITEMS = {
{ text = "Selection 1", key = "1" },
{ text = "Selection 3", key = "3" },
{ text = "Selection 2", key = "2" },
{ text = "Disabled Cell", key = "4", isDisabled = true }
}
it("should create and destroy SmallRadioButtonGroup without errors", function()
local element = mockStyleComponent({
smallRadioButtonGroup = Roact.createElement(SmallRadioButtonGroup, {
onActivated = function() end,
items = ITEMS,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
it("should create and destroy SmallRadioButtonGroup without errors with all optional props used", function()
local element = mockStyleComponent({
smallRadioButtonGroup = Roact.createElement(SmallRadioButtonGroup, {
onActivated = function() end,
items = ITEMS,
selectedValue = "1",
layoutOrder = 1,
}),
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
end
@@ -0,0 +1,7 @@
return {
Small = 16,
Regular = 36,
Large = 48,
XLarge = 96,
XXLarge = 192,
}
@@ -0,0 +1,106 @@
local Carousel = script.Parent
local Container = Carousel.Parent
local App = Container.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local t = require(Packages.t)
local withStyle = require(UIBlox.Style.withStyle)
local GetTextSize = require(UIBlox.Core.Text.GetTextSize)
local IconSize = require(App.ImageSet.Enum.IconSize)
local getIconSize = require(App.ImageSet.getIconSize)
local Images = require(App.ImageSet.Images)
local Core = UIBlox.Core
local Interactable = require(Core.Control.Interactable)
local GenericTextLabel = require(Core.Text.GenericTextLabel.GenericTextLabel)
local ImageSetComponent = require(Core.ImageSet.ImageSetComponent)
local MAX_BOUND = 10000
local SEE_ALL_ARROW = Images["icons/navigation/pushRight_small"]
local TEXT_ICON_PADDING = 4
local CarouselHeader = Roact.PureComponent:extend("CarouselHeader")
CarouselHeader.validateProps = t.strictInterface({
-- The header text for the carousel
headerText = t.optional(t.string),
-- The callback for the see all arrow. if nil, the arrow won't be shown
onSeeAll = t.optional(t.callback),
-- The carousel left margin
carouselMargin = t.optional(t.number),
-- The layout order
layoutOrder = t.optional(t.number),
})
CarouselHeader.defaultProps = {
headerText = "",
carouselMargin = 0,
}
function CarouselHeader:render()
local headerText = self.props.headerText
local onSeeAll = self.props.onSeeAll
local layoutOrder = self.props.layoutOrder
local carouselMargin = self.props.carouselMargin
return withStyle(function(style)
local fontStyle = style.Font.Header1
local baseSize = style.Font.BaseSize
local fontSize = fontStyle.RelativeSize * baseSize
local textFont = fontStyle.Font
local textboxBounds = GetTextSize(headerText, fontSize, textFont, Vector2.new(MAX_BOUND, MAX_BOUND))
local textboxSize = UDim2.fromOffset(textboxBounds.X + TEXT_ICON_PADDING + getIconSize(IconSize.Small),
textboxBounds.Y)
return Roact.createElement("Frame", {
Size = UDim2.new(1, 0, 0, textboxBounds.Y),
BackgroundTransparency = 1,
LayoutOrder = layoutOrder,
}, {
CarouselHeaderButton = Roact.createElement(Interactable, {
Position = UDim2.fromOffset(carouselMargin, 0),
Size = textboxSize,
AutoButtonColor = false,
BackgroundTransparency = 1,
[Roact.Event.Activated] = onSeeAll,
--Note State change is not being used right now.
onStateChanged = function()end,
}, {
Layout = Roact.createElement("UIListLayout", {
FillDirection = Enum.FillDirection.Horizontal,
HorizontalAlignment = Enum.HorizontalAlignment.Left,
VerticalAlignment = Enum.VerticalAlignment.Center,
SortOrder = Enum.SortOrder.LayoutOrder,
Padding = UDim.new(0, TEXT_ICON_PADDING),
}),
HeaderText = Roact.createElement(GenericTextLabel, {
Text = headerText,
TextXAlignment = Enum.TextXAlignment.Left,
TextYAlignment = Enum.TextYAlignment.Center,
LayoutOrder = 1,
fontStyle = fontStyle,
colorStyle = style.Theme.TextEmphasis,
}),
SeeAllArrow = onSeeAll and Roact.createElement(ImageSetComponent.Label, {
Size = UDim2.fromOffset(getIconSize(IconSize.Small), getIconSize(IconSize.Small)),
BackgroundTransparency = 1,
Image = SEE_ALL_ARROW,
ImageColor3 = style.Theme.TextEmphasis.Color,
ImageTransparency = style.Theme.TextEmphasis.Transparency,
LayoutOrder = 2,
}) or nil,
})
})
end)
end
return CarouselHeader
@@ -0,0 +1,40 @@
return function()
local Carousel = script.Parent
local Container = Carousel.Parent
local App = Container.Parent
local UIBlox = App.Parent
local Packages = UIBlox.Parent
local Roact = require(Packages.Roact)
local mockStyleComponent = require(UIBlox.Utility.mockStyleComponent)
local CarouselHeader = require(script.Parent.CarouselHeader)
describe("should create and destroy CarouselHeader with default props without errors", function()
it("should mount and unmount without issue", function()
local element = mockStyleComponent({
Item = Roact.createElement(CarouselHeader)
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
end)
describe("should create and destroy CarouselHeader without errors", function()
it("should mount and unmount without issue", function()
local element = mockStyleComponent({
Item = Roact.createElement(CarouselHeader, {
headerText = "test header",
onSeeAll = function() end,
carouselMargin = 12,
layoutOrder = 1,
})
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
end)
end

Some files were not shown because too many files have changed in this diff Show More