Prechádzať zdrojové kódy

LibWeb: Implement value attribute of RadioNodeList

Shannon Booth 1 rok pred
rodič
commit
f115e44066

+ 47 - 0
Tests/LibWeb/Text/expected/radio-node-list.txt

@@ -0,0 +1,47 @@
+       value1 value2        ===================
+non-checked-with-value
+===================
+2
+value1
+value2
+value = ''
+value = ''
+===================
+checked-with-value
+===================
+2
+value3
+value4
+value = 'value3'
+value = 'value3'
+===================
+checked-no-value
+===================
+2
+on
+on
+value = 'on'
+value = 'on'
+===================
+no-radio-button
+===================
+2
+value1
+value2
+value = ''
+value = ''
+===================
+non-checked-with-value
+===================
+value = 
+value = value1
+===================
+non-checked-no-value
+===================
+value = 
+value = on
+===================
+non-checked-value-on
+===================
+value = 
+value = on

+ 52 - 0
Tests/LibWeb/Text/input/radio-node-list.html

@@ -0,0 +1,52 @@
+<form>
+  <input value="value1" id="non-checked-with-value" type="radio"/>
+  <input value="value2" id="non-checked-with-value" type="radio"/>
+  <input value="value3" id="checked-with-value" type="radio" checked/>
+  <input value="value4" id="checked-with-value" type="radio" checked/>
+  <input value="on" id="non-checked-value-on" type="radio"/>
+  <input value="on" id="non-checked-value-on" type="radio"/>
+  <input value="value1" id="no-radio-button" type="input" checked/>
+  <input value="value2" id="no-radio-button" type="input" checked/>
+  <input id="checked-no-value" type="radio" checked/>
+  <input id="checked-no-value" type="radio" checked/>
+  <input id="non-checked-no-value" type="radio"/>
+  <input id="non-checked-no-value" type="radio"/>
+</form>
+<script src="include.js"></script>
+<script>
+    test(() => {
+        const formElements = document.forms[0].elements;
+
+        function dumpAttributes(name) {
+            println("===================");
+            println(name);
+            println("===================");
+            const list = formElements.namedItem(name);
+            println(list.length);
+            println(list[0].value);
+            println(list[1].value);
+            println(`value = '${list.value}'`);
+            list.value = "on";
+            println(`value = '${list.value}'`);
+        }
+
+        function setValue(name, value) {
+            println("===================");
+            println(name);
+            println("===================");
+            const list = formElements.namedItem(name);
+            println(`value = ${list.value}`);
+            list.value = value;
+            println(`value = ${list.value}`);
+        }
+
+        dumpAttributes("non-checked-with-value");
+        dumpAttributes("checked-with-value");
+        dumpAttributes("checked-no-value");
+        dumpAttributes("no-radio-button");
+
+        setValue("non-checked-with-value", "value1")
+        setValue("non-checked-no-value", "on")
+        setValue("non-checked-value-on", "on")
+    })
+</script>

+ 77 - 0
Userland/Libraries/LibWeb/DOM/RadioNodeList.cpp

@@ -8,6 +8,7 @@
 #include <LibWeb/Bindings/RadioNodeListPrototype.h>
 #include <LibWeb/DOM/Element.h>
 #include <LibWeb/DOM/RadioNodeList.h>
+#include <LibWeb/HTML/HTMLInputElement.h>
 
 namespace Web::DOM {
 
@@ -29,4 +30,80 @@ void RadioNodeList::initialize(JS::Realm& realm)
     set_prototype(&Bindings::ensure_web_prototype<Bindings::RadioNodeListPrototype>(realm, "RadioNodeList"));
 }
 
+static HTML::HTMLInputElement const* radio_button(Node const& node)
+{
+    if (!is<HTML::HTMLInputElement>(node))
+        return nullptr;
+
+    auto const& input_element = verify_cast<HTML::HTMLInputElement>(node);
+    if (input_element.type_state() != HTML::HTMLInputElement::TypeAttributeState::RadioButton)
+        return nullptr;
+
+    return &input_element;
+}
+
+// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#dom-radionodelist-value
+FlyString RadioNodeList::value() const
+{
+    // 1. Let element be the first element in tree order represented by the RadioNodeList object that is an input element whose type
+    //    attribute is in the Radio Button state and whose checkedness is true. Otherwise, let it be null.
+    auto* element = verify_cast<HTML::HTMLInputElement>(first_matching([](Node const& node) -> bool {
+        auto const* button = radio_button(node);
+        if (!button)
+            return false;
+
+        return button->checked();
+    }));
+
+    // 2. If element is null, return the empty string.
+    if (!element)
+        return String {};
+
+    // 3. If element is an element with no value attribute, return the string "on".
+    auto const value = element->get_attribute(HTML::AttributeNames::value);
+    if (value.is_null())
+        return "on"_fly_string;
+
+    // 4. Otherwise, return the value of element's value attribute.
+    return MUST(FlyString::from_deprecated_fly_string(value));
+}
+
+void RadioNodeList::set_value(FlyString const& value)
+{
+    HTML::HTMLInputElement* element = nullptr;
+
+    auto deprecated_value = value.to_deprecated_fly_string();
+
+    // 1. If the new value is the string "on": let element be the first element in tree order represented by the RadioNodeList object
+    //    that is an input element whose type attribute is in the Radio Button state and whose value content attribute is either absent,
+    //    or present and equal to the new value, if any. If no such element exists, then instead let element be null.
+    if (value == "on"sv) {
+        element = verify_cast<HTML::HTMLInputElement>(first_matching([&deprecated_value](auto const& node) {
+            auto const* button = radio_button(node);
+            if (!button)
+                return false;
+
+            auto const value = button->get_attribute(HTML::AttributeNames::value);
+            return value.is_null() || value == deprecated_value;
+        }));
+    }
+    // 2. Otherwise: let element be the first element in tree order represented by the RadioNodeList object that is an input element whose
+    //   type attribute is in the Radio Button state and whose value content attribute is present and equal to the new value, if any. If
+    //   no such element exists, then instead let element be null.
+    else {
+        element = verify_cast<HTML::HTMLInputElement>(first_matching([&deprecated_value](auto const& node) {
+            auto const* button = radio_button(node);
+            if (!button)
+                return false;
+
+            auto const value = button->get_attribute(HTML::AttributeNames::value);
+            return !value.is_null() && value == deprecated_value;
+        }));
+    }
+
+    // 3. If element is not null, then set its checkedness to true.
+    if (element)
+        element->set_checked(true);
+}
+
 }

+ 3 - 0
Userland/Libraries/LibWeb/DOM/RadioNodeList.h

@@ -19,6 +19,9 @@ public:
 
     virtual ~RadioNodeList() override;
 
+    FlyString value() const;
+    void set_value(FlyString const&);
+
 protected:
     virtual void initialize(JS::Realm&) override;
 

+ 1 - 1
Userland/Libraries/LibWeb/DOM/RadioNodeList.idl

@@ -3,5 +3,5 @@
 // https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#radionodelist
 [Exposed=Window, UseNewAKString]
 interface RadioNodeList : NodeList {
-    // FIXME: attribute DOMString value;
+    attribute DOMString value;
 };