diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
index 02c7971..a6aa322 100644
--- a/app/assets/stylesheets/application.css
+++ b/app/assets/stylesheets/application.css
@@ -57,6 +57,11 @@
}
/* NOTE: move to higher priority layer instead of using !important?; add CSS
* @layer requirements in README */
+/* Disable elements with:
+ * `[aria-disabled=true]` - non-form elements (e.g. links),
+ * `[disabled]` - form controls, that may be enabled conditionally,
+ * `[readonly]` - form controls, that stay disabled for form lifetime. */
+[aria-disabled=true],
[disabled] {
border-color: var(--color-border-gray) !important;
color: var(--color-border-gray) !important;
diff --git a/app/javascript/application.js b/app/javascript/application.js
index 09ced32..8318cd9 100644
--- a/app/javascript/application.js
+++ b/app/javascript/application.js
@@ -50,21 +50,30 @@ function formValidate(event) {
window.formValidate = formValidate
-/* Turbo stream actions */
+// Turbo stream actions.
+const FORM_CONTROLS = ["BUTTON", "FIELDSET", "INPUT", "OPTGROUP", "OPTION", "SELECT",
+ "TEXTAREA"]
Turbo.StreamElement.prototype.disableElement = function(element) {
+ if (FORM_CONTROLS.includes(element.tagName)) {
element.setAttribute("disabled", "disabled")
+ } else {
element.setAttribute("aria-disabled", "true")
+ // NOTE: tabindex should not be disabled?
element.setAttribute("tabindex", "-1")
+ }
}
Turbo.StreamActions.disable = function() {
this.targetElements.forEach((e) => { this.disableElement(e) })
}
Turbo.StreamElement.prototype.enableElement = function(element) {
+ if (FORM_CONTROLS.includes(element.tagName)) {
element.removeAttribute("disabled")
+ } else {
element.removeAttribute("aria-disabled")
- // Assume 'tabindex' is not used explicitly, so removing it is safe
+ // Assume 'tabindex' is not used explicitly, so removing it is safe.
element.removeAttribute("tabindex")
+ }
}
Turbo.StreamActions.enable = function() {
this.targetElements.forEach((e) => { this.enableElement(e) })
diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb
index ddcf474..57f9ad3 100644
--- a/test/application_system_test_case.rb
+++ b/test/application_system_test_case.rb
@@ -67,21 +67,11 @@ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
ActiveRecord::Base.establish_connection(TEST_CONFIGS[name.to_sym])
end
- #def assert_stale(element)
- # assert_raises(Selenium::WebDriver::Error::StaleElementReferenceError) { element.tag_name }
- #end
+ Capybara.modify_selector(:link) do
+ expression_filter(:disabled) do |xpath, value|
+ builder(xpath).add_attribute_conditions('aria-disabled': value)
+ end
+ end
- # HTML does not allow [disabled] attribute on tag, so it's not possible to
- # easily find them using e.g. :link selector
- #Capybara.add_selector(:disabled_link) do
- # label " tag with [disabled] attribute"
- #end
- #test "click disabled link" do
- # Link should be unclickable
- # assert_raises(Selenium::WebDriver::Error::ElementClickInterceptedError) do
- # # Use custom selector for disabled links
- # find('a[disabled]').click
- # end
- #end
end
diff --git a/test/system/units_test.rb b/test/system/units_test.rb
index 63862f6..dea2479 100644
--- a/test/system/units_test.rb
+++ b/test/system/units_test.rb
@@ -41,9 +41,10 @@ class UnitsTest < ApplicationSystemTestCase
sign_in
link_labels.slice!(:new_unit, :new_subunit)
type, label = link_labels.to_a.sample
- new_link = all(:link, exact_text: label).sample
- new_link.click
- assert_equal 'disabled', new_link[:disabled]
+ all(:link, exact_text: label).sample.then do |link|
+ link.click
+ link.assert_matches_selector :link, disabled: true
+ end
values = nil
within 'tbody > tr:has(input[type=text], textarea)' do
@@ -74,7 +75,7 @@ class UnitsTest < ApplicationSystemTestCase
assert_no_selector :fillable_field
assert_selector 'tr', count: @user.units.count
end
- assert_no_selector :element, :a, 'disabled': 'disabled',
+ assert_no_selector :link, disabled: true,
exact_text: Regexp.union(link_labels.values)
assert_equal values, Unit.last.attributes.slice(*values.keys)
end
@@ -126,7 +127,7 @@ class UnitsTest < ApplicationSystemTestCase
if type == :edit
assert_no_selector :link, exact_text: link[:text]
else
- assert_equal 'disabled', link[:disabled]
+ link.assert_matches_selector :link, disabled: true
end
within 'tbody > tr:has(input[type=text])' do
@@ -159,7 +160,7 @@ class UnitsTest < ApplicationSystemTestCase
refute subunit_link&.visible?
links[:new_subunit].delete(subunit_link)
else
- assert link[:disabled]
+ link.assert_matches_selector :link, disabled: true
end
type, link = random_link[]
diff --git a/test/system/users_test.rb b/test/system/users_test.rb
index a90bce7..5b281ca 100644
--- a/test/system/users_test.rb
+++ b/test/system/users_test.rb
@@ -8,7 +8,7 @@ class UsersTest < ApplicationSystemTestCase
test 'sign in' do
visit root_url
- assert find_link(href: new_user_session_path)[:disabled]
+ assert_selector :link, text: t(:sign_in), disabled: true
sign_in
assert_no_current_path new_user_session_path
@@ -27,7 +27,7 @@ class UsersTest < ApplicationSystemTestCase
assert_current_path new_user_session_path
assert_text t('devise.failure.invalid', authentication_keys: label.downcase_first)
- assert find_link(href: new_user_session_path)[:disabled]
+ assert_selector :link, text: t(:sign_in), disabled: true
assert has_field?(label, with: email)
end
@@ -89,7 +89,7 @@ class UsersTest < ApplicationSystemTestCase
test 'register' do
visit root_url
click_on t(:register)
- assert find_link(href: new_user_registration_path)[:disabled]
+ assert_selector :link, text: t(:register), disabled: true
fill_in User.human_attribute_name(:email), with: random_email
password = random_password