UI Labs - Modern Storybook Plugin for Roblox

UI Labs (Canary)!

This is a paid version of ui labs that will hold the latest but less tested changes. This will be a direct build of the repository main branch. All of these changes will eventually be moved to the main free plugin.

This version is an opportunity to release changes faster and make testing easier as well as a way to support my work.

If you are unable to purchase this version. You are free to build the latest changes in the repository yourself, There is a small guide in the contributing page of how to do it.


Find the canary version here


Find the original version here

3 Likes

Hello! I have been following this thread for the past few months now. I have been using fusion for my UI ever since I discovered it a year ago.

What is your recommended workflow in integrating this to actual game logic?

Because the way I use fusion right now is by creating UI components in modules and passing the props table. Usually the properties table contain Fusion.Value<...> objects but sometimes it also contains literal functions that I can call inside the component- for example on a button when pressed. It calls the function inside the props table. And what if I want to put a component on a component?

I cannot think of a workflow where my method would work with UILabs. The only way I know I could possibly do this is like this:

--- Story
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Fusion = require(ReplicatedStorage.Fusion)
local ButtonComponent = require(ReplicatedStorage.ButtonComponent)

return {
	fusion = Fusion,
	controls = ButtonComponent.Controls,
	story = ButtonComponent.new,
}
--- ButtonComponent
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Fusion = require(ReplicatedStorage.Fusion)

local button = {}

button.Controls = {
	Visible = true,
	Clicked = false,
}

function button.new(props: {scope: Fusion.Scope} & typeof(button.Controls))
	local Clicked = props.Clicked or props.scope:Value(false)
	local ClickTween = props.scope:Tween(props.scope:Computed(function(use)
		return if use(Clicked) then UDim2.fromScale(1, 1) else UDim2.fromScale(0.5, 0.5)
	end), TweenInfo.new(0.2))
	
	local newButton = props.scope:New("TextButton"){
		Parent = props.target or props.Parent,
		Visible = props.Visible,
		Size = ClickTween,
		[props.scope.OnEvent("MouseButton1Click")] = function()
			if props.OnClick then
				props.OnClick()
			end
		end,
		[props.scope.OnEvent("MouseButton1Down")] = function()
			Clicked:set(true)
		end,
		[props.scope.OnEvent("MouseButton1Up")] = function()
			Clicked:set(false)
		end,
	}
	
	return newButton 
end

return button


Do you have any suggestions on how I would go about this?
I really like how UILabs work and I wanted to integrate it into my project. as it would be really easy to develop UI’s using fusion. Thanks!

1 Like

Uhh HELP??? I can’t open the interface itself, even the widget are nothing!!

I even tried to use UI Labs on my mom’s laptop, but I still couldn’t get the interface to open…
@PepeElToro41 are you able to help?

Hello.

Sorry I missed this.

I’d recommend you only provide your controls in the story instead of defining them in your button component. I also would not directly provide button.new as the story function. so, you can separate the logic for your component and your story.

This is also apparent because your button shouldnt expect a target as this is story only, I would change it like this:

--- Story
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Fusion = require(ReplicatedStorage.Fusion)
local ButtonComponent = require(ReplicatedStorage.ButtonComponent)

local controls = {
	Visible = false,
	Clicked = false
}

return {
	fusion = Fusion,
	controls = controls,
	story = function(props)
		props.scope:New("Frame") {
			Parent = props.Target,
			Size = UDim2.fromScale(1,1),
			BackgroundTransparency = 1
			[Fusion.Children] = ButtonComponent(props.scope, {
				Visible = props.controls.Visible,
				Clicked = props.controls.Clicked,
				OnClick = function()
					print("Clicked") -- mocking OnClick
				end
			})
		}
	end,
}
--- ButtonComponent
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Fusion = require(ReplicatedStorage.Fusion)

local button = {}

type ButtonProps = {
	Visible: boolean,
	Clicked: boolean,
	-- add all the missing types for click callbacks/text/etc
}

function button(scope: Fusion.Scope, props: ButtonProps)
	local Clicked = props.Clicked or scope:Value(false)
	local ClickTween = scope:Tween(scope:Computed(function(use)
		return if use(Clicked) then UDim2.fromScale(1, 1) else UDim2.fromScale(0.5, 0.5)
	end), TweenInfo.new(0.2))
	
	local newButton = scope:New("TextButton"){
		--Parent = props.target or props.Parent (parent is not needed)
		Visible = props.Visible,
		Size = ClickTween,
		[scope.OnEvent("MouseButton1Click")] = function()
			if props.OnClick then
				props.OnClick()
			end
		end,
		[scope.OnEvent("MouseButton1Down")] = function()
			Clicked:set(true)
		end,
		[scope.OnEvent("MouseButton1Up")] = function()
			Clicked:set(false)
		end,
	}
	
	return newButton 
end

PS: I’ve been working in allowing you to directly return the component in the story and it would automatically parent it in the target that way you dont need an extra frame, or always receive a Parent prop, but fusion has been giving me some problems with it . I will eventually add it

1 Like

Hello, What is the problem?. I see the plugin widget there. Is it that it’s too small?

Ah finally, you responded!!
I’m not seeing the actual damn UI Labs interface, no matter what i do (on top, widget), it never shows… Widget is very self-explanatory, it is just small rectangle and shows nothing

Can you show the story you are trying to mount?. You can click the “Show In Explorer” button to see what is being rendered

IT IS the story named “test” (can you actually see the damn number shown). If there was an error from the module i just show in the output, but it’s not there
its just your example from the UI Labs documentation

local Fusion = require(game.ReplicatedStorage.Fusion)
local function story(target: Frame)
	local component = Fusion.New "Frame" {
		Parent = target,
	}

	return function()
		component:Destroy()
	end
end

return story

but then after trying again i got this error

  18:47:43.539  ERR [Fusion] To create instances using New, provide a scope. (e.g. `myScope:New "Frame" { ... }`). See discussion #292 on GitHub for advice. 
    ID: scopeMissing
    Learn more: https://elttob.uk/Fusion/0.3/api-reference/general/errors/#scopemissing
[string "ReplicatedStorage.Fusion.External"]:91 function logError
[string "ReplicatedStorage.Fusion.Instances.New"]:25 function New
[string "ReplicatedStorage.Folder.test.story"]:3 function story
  -  Edit
  18:47:43.540  [UI Labs]: Functional story errored when called.


[Fusion] To create instances using New, provide a scope. (e.g. `myScope:New "Frame" { ... }`). See discussion #292 on GitHub for advice. 
    ID: scopeMissing
    Learn more: https://elttob.uk/Fusion/0.3/api-reference/general/errors/#scopemissing
[string "ReplicatedStorage.Fusion.External"]:91 function logError
[string "ReplicatedStorage.Fusion.Instances.New"]:25 function New
[string "ReplicatedStorage.Folder.test.story"]:3 function story
  -  Edit

however even without the component stuff i cant even open the ui labs interface

The error

It’s telling you you forgot a scope. Scopes - Fusion

so i fixed it, but even with that i still cant open the goddam UI (No errors on the output)

Howdy,

I think your setup may be a bit incomplete for Fusions setup. Ensure you’re following the UI Labs Docs for Fusion varying on your version (0.3 does not require a cleanup function as it cleans up every re-render.

Fusion Stories always pass an array of props i.e;

export type Scope = Fusion.Scope<typeof(Fusion)>
export type StoryProps = {
	scope: Scope,
	target: Instance,
	controls: typeof(controls)
}

local controls = {
  isVisible = true -- this is turned into a state obj, useful for mock states, optional
}
local function story(props: StoryProps)
	-- Best practice, I always use innerScope for Parent-Child like hierarchies/deriveScope to maintain its own (dropdown menus, context, etc)
	local Scope = props.scope:innerScope()
	Scope:New "Frame" {
		Parent = props.target -- Important, pass the target from the StoryProps
	}
end
1 Like

Try clicking the “Show on explorer” you will see your story there. It’s probably that the default size for “Frame” in fusion is 0, 0.

And yes, you are using fusion incorrectly, as you are using fusion 0.3.

I’d recommend reading fusions documentation. as well as This part in the ui labs docs