jsdotlua / react-lua

A comprehensive, but not exhaustive, translation of upstream ReactJS 17.x into Lua.
https://jsdotlua.github.io/react-lua/
MIT License
428 stars 15 forks source link

`React.Attributes` should be implemented #38

Open Nidoxs opened 1 week ago

Nidoxs commented 1 week ago

This will allow attributes to be applied to instances. While I've not had a need for this myself, there's others who have and I think it'd be an appropriate addition to the prop marker symbols.

Example:

return e("TextLabel", {
    [React.Attributes] = {
        Test = 100,
        MyBool = false
    }
})

Draft PR for full changes proposed:

39

Nidoxs commented 1 week ago

A new PropMarker symbol would need to be created for attributes:

local Symbol = require("../Symbol.roblox.luau")

local Attributes = Symbol.named("RobloxAttributes")

return Attributes

I've prototyped this on my end and I propose the following function to be added to modules\react-roblox\src\client\roblox\RobloxComponentProps.luau

-- these types could be put somewhere else in a more accessible file
type AcceptableAttributeType =
    string
    | boolean
    | number
    | UDim
    | UDim2
    | BrickColor
    | Color3
    | Vector2
    | Vector3
    | EnumItem
type AttributesDictionary = { [string]: AcceptableAttributeType }

local function applyAttributes(
    hostInstance: Instance,
    oldAttributes: AttributesDictionary,
    newAttributes: AttributesDictionary
)
    if __DEV__ then
        if newAttributes ~= nil then
            if typeof(newAttributes) ~= "table" then
                console.error(
                    "Type provided for ReactRoblox.Attributes is invalid - attributes should be "
                        .. "specified as a dictionary, where the key-value pairs represent the "
                        .. "attribute names and their respective values. Instead received:\n%s",
                    inspect(newAttributes)
                )
            end
            return
        end
    end

    if oldAttributes then
        for attributeName in oldAttributes do
            if newAttributes == nil or newAttributes[attributeName] == nil then
                hostInstance:SetAttribute(attributeName, nil)
            end
        end
    end

    if newAttributes then
        for attributeName, value in newAttributes do
            hostInstance:SetAttribute(attributeName, value)
        end
    end
end

I would have done this work myself, but I'm unsure on the testing workflow here and I don't want to contribute work that I cannot write tests for.

It also seems that the original team working on react-lua anticipated testing some behaviours outside of the Roblox DataModel / environment, as I have come across a module that mocks the behaviour of CollectionService tags when it comes to adding, updating and removing them. See https://github.com/jsdotlua/react-lua/blob/main/modules/react-test-renderer/src/roblox/RobloxComponentProps.luau

The point here is that there should probably be a similar thing for mocking attributes being set, updated and removed. I am unsure if this is needed though.