Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
80b4273
link widget to widget3
ElectricalBoy May 11, 2026
1e52b90
extract HtmlProps type
ElectricalBoy May 11, 2026
ece5c50
copytoclipboard widget to widget3
ElectricalBoy May 11, 2026
7283c79
button to widget3
ElectricalBoy May 11, 2026
0e4d240
dialog to widget3
ElectricalBoy May 11, 2026
f1a9cef
label to widget3
ElectricalBoy May 11, 2026
8ac7fbe
slider to widget3
ElectricalBoy May 11, 2026
bb4b2f7
carousel to widget3
ElectricalBoy May 11, 2026
96b1f9b
update type
ElectricalBoy May 11, 2026
86e399c
box to widget3
ElectricalBoy May 11, 2026
bb20959
dropdown item to widget3
ElectricalBoy May 11, 2026
a817bbd
dropdown container to widget3
ElectricalBoy May 11, 2026
79768a4
fontawesome icon to widget3
ElectricalBoy May 11, 2026
4abc452
image widget to widget3
ElectricalBoy May 11, 2026
948737a
access modifier
ElectricalBoy May 11, 2026
b5cd4b6
fix type definition
ElectricalBoy May 11, 2026
3f0342e
type casting
ElectricalBoy May 11, 2026
7eeb77b
fix type annotation
ElectricalBoy May 11, 2026
6e990d4
datatable to widget3
ElectricalBoy May 11, 2026
47e1511
collapsible chevron toggle to widget3
ElectricalBoy May 11, 2026
a4cb42e
collapsible toggle to widget3
ElectricalBoy May 11, 2026
def54c1
collapsible to widget3
ElectricalBoy May 11, 2026
65b934b
rename html props type
ElectricalBoy May 11, 2026
52f9906
correctly pass props
ElectricalBoy May 11, 2026
417840f
slightly better
ElectricalBoy May 11, 2026
18b4bdc
fix dropdown
ElectricalBoy May 11, 2026
3fc79d6
fix test
ElectricalBoy May 11, 2026
8ae0b3b
kick class anno
ElectricalBoy May 11, 2026
5eeb9a4
revert rendering in test
ElectricalBoy May 11, 2026
1d753be
attempt 2
ElectricalBoy May 11, 2026
7923211
cast non-nil
ElectricalBoy May 11, 2026
88e247e
attempt 3
ElectricalBoy May 11, 2026
6b563e4
suggestion from feedback
ElectricalBoy May 11, 2026
32bb3c4
adjust access
ElectricalBoy May 11, 2026
2937b0e
fix link widget misbehaving
ElectricalBoy May 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lua/definitions/mw.lua
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ function mw.html:tag(tagName, args) end
---@param name string
---@param value string|number|nil
---@return self
---@overload fun(self, param: {[string]: string})
---@overload fun(self, param: {[string]: string|number|nil})
function mw.html:attr(name, value) end

---Get the value of a html attribute previously set using html:attr() with the given name.
Expand Down
13 changes: 11 additions & 2 deletions lua/spec/infobox_shop_merch_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ local ShopMerch = require('Module:Widget/Infobox/ShopMerch')
local function render(args)
local widget = ShopMerch{args = args}
local rendered = widget:render()
return rendered and mw.text.jsonEncode(rendered) or nil
if not rendered then
return
end
for i in ipairs(rendered) do
rendered[i] = tostring(rendered[i])
end
return mw.text.jsonEncode(rendered)
end

describe('Infobox/ShopMerch', function()
Expand Down Expand Up @@ -37,12 +43,14 @@ describe('Infobox/ShopMerch', function()
it('uses default shop url when shoplink=true', function()
local output = render{shoplink = 'true'}
assert.is_not_nil(output)
---@cast output -nil
assert.is_truthy(output:find('https://links.liquipedia.net/tlstore', 1, true))
end)

it('strips leading slashes from slugs', function()
local output = render{shoplink = '/test'}
assert.is_not_nil(output)
---@cast output -nil
assert.is_truthy(output:find('https://links.liquipedia.net/test', 1, true))

local output2 = render{shoplink = '///test'}
Expand All @@ -61,6 +69,7 @@ describe('Infobox/ShopMerch', function()
-- 'ö' is 2 bytes in Lua, but %C3%B6 is 6 chars in URL
local output = render{shoplink = 'töst'}
assert.is_not_nil(output)
---@cast output -nil
assert.is_truthy(output:find('https://links.liquipedia.net/t%C3%B6st', 1, true))
end)

Expand Down Expand Up @@ -89,4 +98,4 @@ describe('Infobox/ShopMerch', function()
local expanding_slug = string.rep('ö', 500)
assert.has_error(function() render{shoplink = expanding_slug} end)
end)
end)
end)
41 changes: 18 additions & 23 deletions lua/wikis/commons/Widget/Basic/Box.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
local Lua = require('Module:Lua')

local Array = Lua.import('Module:Array')
local Class = Lua.import('Module:Class')

local HtmlWidgets = Lua.import('Module:Widget/Html/All')
local Widget = Lua.import('Module:Widget')
local Component = Lua.import('Module:Widget/Component')
local Html = Lua.import('Module:Widget/Html')

---@class BoxProps
---@field children Renderable[]|Renderable
Expand All @@ -22,36 +21,32 @@ local Widget = Lua.import('Module:Widget')
---@field width string?
---@field height string?

---@class Box: Widget
---@operator call(BoxProps): Box
---@field props BoxProps
local Box = Class.new(Widget)

---@return Widget|Renderable
function Box:render()
local children = self.props.children
---@param props BoxProps
---@return Renderable
local function Box(props)
local children = props.children
if not Array.isArray(children) then
return self.props.children
return props.children
end
---@cast children -Renderable
---@cast children Renderable[]

return HtmlWidgets.Div{
css = {['max-width'] = self.props.maxWidth},
return Html.Div{
css = {['max-width'] = props.maxWidth},
children = Array.map(children, function(child)
return HtmlWidgets.Div{
return Html.Div{
classes = {'template-box'},
css = {
['padding-left'] = self.props.paddingLeft,
['padding-bottom'] = self.props.paddingBottom,
['padding-right'] = self.props.paddingRight,
width = self.props.width,
height = self.props.height,
overflow = self.props.height and 'hidden' or nil,
['padding-left'] = props.paddingLeft,
['padding-bottom'] = props.paddingBottom,
['padding-right'] = props.paddingRight,
width = props.width,
height = props.height,
overflow = props.height and 'hidden' or nil,
},
children = child,
}
end)
}
end

return Box
return Component.component(Box)
65 changes: 31 additions & 34 deletions lua/wikis/commons/Widget/Basic/Button.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,97 +8,94 @@
local Lua = require('Module:Lua')

local Array = Lua.import('Module:Array')
local Class = Lua.import('Module:Class')
local Logic = Lua.import('Module:Logic')
local Table = Lua.import('Module:Table')

local Widget = Lua.import('Module:Widget')
local HtmlWidgets = Lua.import('Module:Widget/Html/All')
local Component = Lua.import('Module:Widget/Component')
local Html = Lua.import('Module:Widget/Html')
local Link = Lua.import('Module:Widget/Basic/Link')
local Div = HtmlWidgets.Div
local Div = Html.Div

---@class ButtonWidgetParameters
---@class ButtonWidgetParameters: HtmlNodeProps
---@field title string?
---@field link string?
---@field linktype 'internal'|'external'|nil
---@field variant 'primary'|'secondary'|'themed'|'ghost'|'destructive'|nil
---@field variant 'primary'|'secondary'|'themed'|'ghost'|'destructive'|'icon'|nil
---@field size 'xs'|'sm'|'md'|'lg'|nil
---@field grow boolean?
---@field aligncontent 'left'|'right'|nil

---@class ButtonWidget: Widget
---@operator call(ButtonWidgetParameters): ButtonWidget
local Button = Class.new(Widget)
Button.defaultProps = {
local defaultProps = {
linktype = 'internal',
variant = 'primary',
size = 'md',
grow = false, -- Whether the button should grow to fill the available space
aligncontent = nil,
}

---@return Widget
function Button:render()
---@param props ButtonWidgetParameters
---@return HtmlNode
local function Button(props)
--- MW Parser does not allowed the <button> tag, so we use a <div>
local cssClasses = {'btn'}
if self.props.variant == 'primary' then
if props.variant == 'primary' then
table.insert(cssClasses, 'btn-primary')
elseif self.props.variant == 'secondary' then
elseif props.variant == 'secondary' then
table.insert(cssClasses, 'btn-secondary')
elseif self.props.variant == 'themed' then
elseif props.variant == 'themed' then
table.insert(cssClasses, 'btn-themed')
elseif self.props.variant == 'ghost' then
elseif props.variant == 'ghost' then
table.insert(cssClasses, 'btn-ghost')
elseif self.props.variant == 'destructive' then
elseif props.variant == 'destructive' then
table.insert(cssClasses, 'btn-destructive')
end

if self.props.size == 'xs' then
if props.size == 'xs' then
table.insert(cssClasses, 'btn-extrasmall')
elseif self.props.size == 'sm' then
elseif props.size == 'sm' then
table.insert(cssClasses, 'btn-small')
elseif self.props.size == 'lg' then
elseif props.size == 'lg' then
table.insert(cssClasses, 'btn-large')
end

local cssTable = {}
if self.props.grow then
if props.grow then
cssTable.width = '100%'
end
if self.props.aligncontent == 'left' then
if props.aligncontent == 'left' then
cssTable['justify-content'] = 'left'
elseif self.props.aligncontent == 'right' then
elseif props.aligncontent == 'right' then
cssTable['justify-content'] = 'right'
end

local button = Div{
css = Logic.nilIfEmpty(cssTable),
classes = Array.extend(cssClasses, self.props.classes or {}),
classes = Array.extend(cssClasses, props.classes or {}),
attributes = Table.merge({
title = self.props.title,
['aria-label'] = self.props.title,
title = props.title,
['aria-label'] = props.title,
role = 'button',
tabindex = '0',
}, self.props.attributes or {}),
children = self.props.children,
}, props.attributes or {}),
children = props.children,
}

if not self.props.link then
if not props.link then
return button
end

-- Have to wrap it in an extra div to prevent the mediawiki parser from messing it up
return Div{
css = self.props.grow and {flex = '1'} or nil,
classes = self.props.classes or {},
css = props.grow and {flex = '1'} or nil,
classes = props.classes or {},
children = {
Link{
link = self.props.link,
linktype = self.props.linktype,
link = props.link,
linktype = props.linktype,
children = {button},
}
}
}
end

return Button
return Component.component(Button, defaultProps)
38 changes: 16 additions & 22 deletions lua/wikis/commons/Widget/Basic/Carousel.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@
local Lua = require('Module:Lua')

local Array = Lua.import('Module:Array')
local Class = Lua.import('Module:Class')
local Table = Lua.import('Module:Table')

local Widget = Lua.import('Module:Widget')
local Component = Lua.import('Module:Widget/Component')
local IconWidget = Lua.import('Module:Widget/Image/Icon/Fontawesome')
local Button = Lua.import('Module:Widget/Basic/Button')
local HtmlWidgets = Lua.import('Module:Widget/Html/All')
local Div = HtmlWidgets.Div
local Span = HtmlWidgets.Span
local Html = Lua.import('Module:Widget/Html')
local Div = Html.Div
local Span = Html.Span

---@class CarouselWidgetParameters
---@field children Renderable[]
Expand All @@ -25,34 +24,29 @@ local Span = HtmlWidgets.Span
---@field classes string[]?
---@field css table?

---@class CarouselWidget: Widget
---@operator call(CarouselWidgetParameters): CarouselWidget
---@field props CarouselWidgetParameters
local Carousel = Class.new(Widget)
Carousel.defaultProps = {
local defaultProps = {
itemWidth = '200px',
gap = '0.5rem',
classes = {},
css = {},
}

---@return Widget
function Carousel:render()
assert(self.props.children, 'Carousel: children is required')
assert(Array.isArray(self.props.children), 'Carousel: children must be an array')
---@param props CarouselWidgetParameters
---@return HtmlNode
local function Carousel(props)
assert(props.children, 'Carousel: children is required')
assert(Array.isArray(props.children), 'Carousel: children must be an array')

local carouselCss = Table.mergeInto({
gap = self.props.gap,
}, self.props.css)
gap = props.gap,
}, props.css)

local carouselContent = Div{
classes = {'carousel-content'},
css = carouselCss,
children = Array.map(self.props.children, function(child)
children = Array.map(props.children, function(child)
return Div{
classes = {'carousel-item'},
css = {
width = self.props.itemWidth,
width = props.itemWidth,
},
children = {child},
}
Expand Down Expand Up @@ -87,7 +81,7 @@ function Carousel:render()
local rightFade = Div{classes = {'carousel-fade', 'carousel-fade--right'}}

return Div{
classes = Array.extend({'carousel'}, self.props.classes),
classes = Array.extendWith({'carousel'}, props.classes),
children = {
leftButton,
rightButton,
Expand All @@ -98,4 +92,4 @@ function Carousel:render()
}
end

return Carousel
return Component.component(Carousel, defaultProps)
25 changes: 9 additions & 16 deletions lua/wikis/commons/Widget/Basic/CopyToClipboard.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,32 @@

local Lua = require('Module:Lua')

local Class = Lua.import('Module:Class')

local Widget = Lua.import('Module:Widget')
local HtmlWidgets = Lua.import('Module:Widget/Html/All')
local Span = HtmlWidgets.Span
local Component = Lua.import('Module:Widget/Component')
local Html = Lua.import('Module:Widget/Html')
local Span = Html.Span

---@class CopyToClipboardProps
---@field children Renderable|Renderable[]?
---@field textToCopy string? text to be copied to clipboard
---@field successText string?

---@class CopyToClipboardWidget: Widget
---@operator call(CopyToClipboardProps): CopyToClipboardWidget
---@field props CopyToClipboardProps
local CopyToClipboard = Class.new(Widget)

---@return Widget
function CopyToClipboard:render()
local props = self.props
---@param props CopyToClipboardProps
---@return HtmlNode
local function CopyToClipboard(props)
return Span{
classes = {'copy-to-clipboard'},
attributes = {['data-copied-text'] = props.successText},
children = {
Span{
classes = {'copy-this'},
children = self.props.textToCopy,
children = props.textToCopy,
},
Span{
classes = {'see-this'},
children = self.props.children,
children = props.children,
}
}
}
end

return CopyToClipboard
return Component.component(CopyToClipboard)
Loading
Loading