From 4ad8d39e326f9b25c14310c6721f6860519610e5 Mon Sep 17 00:00:00 2001 From: Tyler Burke Date: Wed, 29 Oct 2025 14:52:01 -0600 Subject: [PATCH 1/6] Enhance CopyComponent accessibility by adding ARIA attributes and keyboard interaction support --- .../components/InlineEditLinkComponent.java | 54 +++++++++++++++++++ src/main/resources/META-INF/faces.taglib.xml | 45 ++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java diff --git a/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java b/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java new file mode 100644 index 0000000..56363b4 --- /dev/null +++ b/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java @@ -0,0 +1,54 @@ +package co.cfly.faces.components; + +import java.io.IOException; + +import co.cfly.faces.utils.RendererTools; +import jakarta.faces.component.FacesComponent; +import jakarta.faces.component.UIComponent; +import jakarta.faces.context.FacesContext; +import jakarta.faces.context.ResponseWriter; + +@FacesComponent(value = "co.cfly.faces.components.InlineEditLinkComponent", namespace = Families.NAMESPACE) +public class InlineEditLinkComponent extends ComponentBase{ + + @Override + public String getFamily() { + return Families.OUTPUT_COMPONENT_FAMILY; + } + + @Override + public boolean getRendersChildren() { + return true; + } + + @Override + public void encodeBegin(FacesContext context) throws IOException{ + ResponseWriter writer = context.getResponseWriter(); + writer.startElement("a", this); + writeId(context); + final String computedStyleClass = RendererTools.spaceSeperateStrings("popover-source", (String) getAttributes().get("styleClass")); + writeAttribute("class", (computedStyleClass), context); + writeAttributeIfExists("style", "style", context); + writeStandardAttributes(context); + writeAttributeIfExistsOrDefault("placement", "data-bs-placement", "right", context); + writeAttributeIfExistsOrDefault("trigger", "data-bs-trigger", "hover", context); + writeAttributeIfExistsOrDefault("html", "data-bs-html", "true", context); + writeAttributeIfExistsOrDefault("delay", "data-bs-delay", "0", context); + writeAttributeIfExists("content", "data-bs-content", context); + writeAttributeIfExistsOrDefault("container", "data-bs-container", "false", context); + + if (getAttributes().get("modal") instanceof UIComponent modalComponent) { + writeAttribute("data-bs-toggle", "modal", context); + writeAttribute("data-bs-target", "#" + modalComponent.getClientId(), context); + } + } + + @Override + public void encodeEnd(FacesContext context) throws IOException { + final boolean editable = getAttribute("editable", true); + final ResponseWriter writer = context.getResponseWriter(); + if (editable) { + writer.endElement("a"); + } + } +} diff --git a/src/main/resources/META-INF/faces.taglib.xml b/src/main/resources/META-INF/faces.taglib.xml index 275e34e..2c68936 100644 --- a/src/main/resources/META-INF/faces.taglib.xml +++ b/src/main/resources/META-INF/faces.taglib.xml @@ -552,4 +552,49 @@ boolean + + inlineEditLink + + co.cfly.faces.components.InlineEditLinkComponent + + + editable + boolean + + + CSS Style + style + java.lang.String + + + CSS Style Classes + styleClass + java.lang.String + + + content + true + java.lang.String + + + placement + java.lang.String + + + trigger + java.lang.String + + + html + java.lang.String + + + delay + java.lang.String + + + container + java.lang.String + + From 1594c317d16ffd09db6691c61eaa6bdd0eca7946 Mon Sep 17 00:00:00 2001 From: Tyler Burke Date: Wed, 5 Nov 2025 12:01:58 -0700 Subject: [PATCH 2/6] Add modal attribute to InlineEditLinkComponent for Bootstrap integration --- .../components/InlineEditLinkComponent.java | 51 +++++++++++-------- src/main/resources/META-INF/faces.taglib.xml | 6 +++ 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java b/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java index 56363b4..53a2984 100644 --- a/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java +++ b/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java @@ -9,7 +9,7 @@ import jakarta.faces.context.ResponseWriter; @FacesComponent(value = "co.cfly.faces.components.InlineEditLinkComponent", namespace = Families.NAMESPACE) -public class InlineEditLinkComponent extends ComponentBase{ +public class InlineEditLinkComponent extends ComponentBase { @Override public String getFamily() { @@ -22,33 +22,44 @@ public boolean getRendersChildren() { } @Override - public void encodeBegin(FacesContext context) throws IOException{ - ResponseWriter writer = context.getResponseWriter(); - writer.startElement("a", this); - writeId(context); - final String computedStyleClass = RendererTools.spaceSeperateStrings("popover-source", (String) getAttributes().get("styleClass")); - writeAttribute("class", (computedStyleClass), context); - writeAttributeIfExists("style", "style", context); - writeStandardAttributes(context); - writeAttributeIfExistsOrDefault("placement", "data-bs-placement", "right", context); - writeAttributeIfExistsOrDefault("trigger", "data-bs-trigger", "hover", context); - writeAttributeIfExistsOrDefault("html", "data-bs-html", "true", context); - writeAttributeIfExistsOrDefault("delay", "data-bs-delay", "0", context); - writeAttributeIfExists("content", "data-bs-content", context); - writeAttributeIfExistsOrDefault("container", "data-bs-container", "false", context); - - if (getAttributes().get("modal") instanceof UIComponent modalComponent) { - writeAttribute("data-bs-toggle", "modal", context); - writeAttribute("data-bs-target", "#" + modalComponent.getClientId(), context); + public void encodeBegin(FacesContext context) throws IOException { + final ResponseWriter writer = context.getResponseWriter(); + final boolean editable = getAttribute("editable", true); + if (editable) { + writer.startElement("span", this); + writeId(context); + + String styleClass = (String) getAttributes().getOrDefault("styleClass", ""); + String styleClassValue = RendererTools.spaceSeperateStrings("inline-edit popover-source", styleClass); + writeAttribute("class", styleClassValue, context); + + writeAttributeIfExists("style", "style", context); + writeStandardAttributes(context); + + // Popover attributes for JS initialization + writeAttribute("data-bs-dual-toggle", "popover", context); + writeAttributeIfExistsOrDefault("placement", "data-bs-placement", "right", context); + writeAttributeIfExistsOrDefault("trigger", "data-bs-trigger", "hover", context); + writeAttributeIfExistsOrDefault("html", "data-bs-html", "true", context); + writeAttributeIfExistsOrDefault("delay", "data-bs-delay", "{\"show\":0,\"hide\":5000}", context); + writeAttributeIfExists("content", "data-bs-content", context); + writeAttributeIfExistsOrDefault("container", "data-bs-container", "false", context); + + // Modal attributes for Bootstrap + if (getAttributes().get("modal") instanceof UIComponent modalComponent) { + writeAttribute("data-bs-toggle", "modal", context); + writeAttribute("data-bs-target", "#" + modalComponent.getClientId(), context); + } } } + @Override public void encodeEnd(FacesContext context) throws IOException { final boolean editable = getAttribute("editable", true); final ResponseWriter writer = context.getResponseWriter(); if (editable) { - writer.endElement("a"); + writer.endElement("span"); } } } diff --git a/src/main/resources/META-INF/faces.taglib.xml b/src/main/resources/META-INF/faces.taglib.xml index 63a9202..1aa84d4 100644 --- a/src/main/resources/META-INF/faces.taglib.xml +++ b/src/main/resources/META-INF/faces.taglib.xml @@ -609,6 +609,12 @@ true java.lang.String + + Modal Component which will be toggled when clicked + modal + true + jakarta.faces.component.UIComponent + placement java.lang.String From 183ce082802222ff6c5a7f7d430fa2b8a2f5b699 Mon Sep 17 00:00:00 2001 From: Tyler Burke Date: Wed, 5 Nov 2025 15:19:09 -0700 Subject: [PATCH 3/6] --- Not Working --- Add label attribute to InlineEditLinkComponent for enhanced functionality --- .../components/InlineEditLinkComponent.java | 22 +++++++++++++++++-- src/main/resources/META-INF/faces.taglib.xml | 4 ++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java b/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java index 53a2984..4ad1a51 100644 --- a/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java +++ b/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java @@ -42,18 +42,36 @@ public void encodeBegin(FacesContext context) throws IOException { writeAttributeIfExistsOrDefault("trigger", "data-bs-trigger", "hover", context); writeAttributeIfExistsOrDefault("html", "data-bs-html", "true", context); writeAttributeIfExistsOrDefault("delay", "data-bs-delay", "{\"show\":0,\"hide\":5000}", context); - writeAttributeIfExists("content", "data-bs-content", context); writeAttributeIfExistsOrDefault("container", "data-bs-container", "false", context); - // Modal attributes for Bootstrap if (getAttributes().get("modal") instanceof UIComponent modalComponent) { writeAttribute("data-bs-toggle", "modal", context); writeAttribute("data-bs-target", "#" + modalComponent.getClientId(), context); } + + // Write label text + String label = (String) getAttributes().getOrDefault("label", ""); + writer.writeText(label, "label"); + } + } + + @Override + public void encodeChildren(FacesContext context) throws IOException { + final boolean editable = getAttribute("editable", true); + if (editable && getChildCount() > 0) { + final ResponseWriter writer = context.getResponseWriter(); + writer.startElement("div", this); + writeAttribute("style", "display:none;", context); // Hidden by default + for (UIComponent child : getChildren()) { + child.encodeAll(context); + } + writer.endElement("div"); + // writeAttributeIfExists(this, "data-bs-content", context); } } + @Override public void encodeEnd(FacesContext context) throws IOException { final boolean editable = getAttribute("editable", true); diff --git a/src/main/resources/META-INF/faces.taglib.xml b/src/main/resources/META-INF/faces.taglib.xml index 1aa84d4..d0417f8 100644 --- a/src/main/resources/META-INF/faces.taglib.xml +++ b/src/main/resources/META-INF/faces.taglib.xml @@ -635,5 +635,9 @@ container java.lang.String + + label + java.lang.String + From 7fed284fb180451ea81842228e08b9ed42d95600 Mon Sep 17 00:00:00 2001 From: Tyler Burke Date: Thu, 6 Nov 2025 12:04:17 -0700 Subject: [PATCH 4/6] Refactor InlineEditLinkComponent to add label attribute and improve modal handling --- .../components/InlineEditLinkComponent.java | 22 +++++++++++++++++-- src/main/resources/META-INF/faces.taglib.xml | 9 ++++---- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java b/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java index 53a2984..de40378 100644 --- a/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java +++ b/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java @@ -42,17 +42,35 @@ public void encodeBegin(FacesContext context) throws IOException { writeAttributeIfExistsOrDefault("trigger", "data-bs-trigger", "hover", context); writeAttributeIfExistsOrDefault("html", "data-bs-html", "true", context); writeAttributeIfExistsOrDefault("delay", "data-bs-delay", "{\"show\":0,\"hide\":5000}", context); - writeAttributeIfExists("content", "data-bs-content", context); writeAttributeIfExistsOrDefault("container", "data-bs-container", "false", context); - // Modal attributes for Bootstrap if (getAttributes().get("modal") instanceof UIComponent modalComponent) { writeAttribute("data-bs-toggle", "modal", context); writeAttribute("data-bs-target", "#" + modalComponent.getClientId(), context); } + + // Write label text + String label = (String) getAttributes().getOrDefault("label", "test label"); + writer.writeText(label, "label"); + + // Render children in a hidden template for popover content + writer.startElement("template", this); + writer.writeAttribute("id", getClientId(context) + "_popoverContent", null); + for (UIComponent child : getChildren()) { + child.encodeAll(context); + } + writer.endElement("template"); + + // Reference the template in the popover content attribute + writeAttribute("data-bs-content", "document.getElementById('" + getClientId(context) + "_popoverContent').innerHTML", context); } } + @Override + public void encodeChildren(FacesContext context) { + // Since Children are rendered manually in the encodeBegin we don't want to render them twice + } + @Override public void encodeEnd(FacesContext context) throws IOException { diff --git a/src/main/resources/META-INF/faces.taglib.xml b/src/main/resources/META-INF/faces.taglib.xml index 1aa84d4..5bb00c6 100644 --- a/src/main/resources/META-INF/faces.taglib.xml +++ b/src/main/resources/META-INF/faces.taglib.xml @@ -604,11 +604,6 @@ styleClass java.lang.String - - content - true - java.lang.String - Modal Component which will be toggled when clicked modal @@ -635,5 +630,9 @@ container java.lang.String + + label + java.lang.String + From af360a2c05dee097c23ba55c97b3157eabd45447 Mon Sep 17 00:00:00 2001 From: Tyler Burke Date: Fri, 7 Nov 2025 12:16:44 -0700 Subject: [PATCH 5/6] Add title attribute to InlineEditLinkComponent and adjust popover settings --- .../co/cfly/faces/components/InlineEditLinkComponent.java | 6 +++--- src/main/resources/META-INF/faces.taglib.xml | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java b/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java index de40378..56e6da7 100644 --- a/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java +++ b/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java @@ -38,10 +38,10 @@ public void encodeBegin(FacesContext context) throws IOException { // Popover attributes for JS initialization writeAttribute("data-bs-dual-toggle", "popover", context); - writeAttributeIfExistsOrDefault("placement", "data-bs-placement", "right", context); - writeAttributeIfExistsOrDefault("trigger", "data-bs-trigger", "hover", context); + writeAttributeIfExistsOrDefault("title", "data-bs-title", "test title", context); + writeAttributeIfExistsOrDefault("placement", "data-bs-placement", "bottom", context); writeAttributeIfExistsOrDefault("html", "data-bs-html", "true", context); - writeAttributeIfExistsOrDefault("delay", "data-bs-delay", "{\"show\":0,\"hide\":5000}", context); + writeAttributeIfExistsOrDefault("delay", "data-bs-delay", "{\"show\":500,\"hide\":5000}", context); writeAttributeIfExistsOrDefault("container", "data-bs-container", "false", context); if (getAttributes().get("modal") instanceof UIComponent modalComponent) { diff --git a/src/main/resources/META-INF/faces.taglib.xml b/src/main/resources/META-INF/faces.taglib.xml index 5bb00c6..551f69c 100644 --- a/src/main/resources/META-INF/faces.taglib.xml +++ b/src/main/resources/META-INF/faces.taglib.xml @@ -634,5 +634,9 @@ label java.lang.String + + title + java.lang.String + From 9179be97e079262b0e72fe46b31096a22182a882 Mon Sep 17 00:00:00 2001 From: Tyler Burke Date: Thu, 13 Nov 2025 12:16:27 -0700 Subject: [PATCH 6/6] Add title attribute to InlineEditLinkComponent and adjust popover settings --- pom.xml | 2 +- .../components/InlineEditLinkComponent.java | 83 ------------------- .../components/PopoverContainerComponent.java | 75 +++++++++++++++++ src/main/resources/META-INF/faces.taglib.xml | 34 +------- 4 files changed, 78 insertions(+), 116 deletions(-) delete mode 100644 src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java create mode 100644 src/main/java/co/cfly/faces/components/PopoverContainerComponent.java diff --git a/pom.xml b/pom.xml index a73152a..fc5c9a0 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ co.cfly jsf-components jar - 6.0.10 + 6.0.11-SNAPSHOT JSF Components diff --git a/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java b/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java deleted file mode 100644 index 56e6da7..0000000 --- a/src/main/java/co/cfly/faces/components/InlineEditLinkComponent.java +++ /dev/null @@ -1,83 +0,0 @@ -package co.cfly.faces.components; - -import java.io.IOException; - -import co.cfly.faces.utils.RendererTools; -import jakarta.faces.component.FacesComponent; -import jakarta.faces.component.UIComponent; -import jakarta.faces.context.FacesContext; -import jakarta.faces.context.ResponseWriter; - -@FacesComponent(value = "co.cfly.faces.components.InlineEditLinkComponent", namespace = Families.NAMESPACE) -public class InlineEditLinkComponent extends ComponentBase { - - @Override - public String getFamily() { - return Families.OUTPUT_COMPONENT_FAMILY; - } - - @Override - public boolean getRendersChildren() { - return true; - } - - @Override - public void encodeBegin(FacesContext context) throws IOException { - final ResponseWriter writer = context.getResponseWriter(); - final boolean editable = getAttribute("editable", true); - if (editable) { - writer.startElement("span", this); - writeId(context); - - String styleClass = (String) getAttributes().getOrDefault("styleClass", ""); - String styleClassValue = RendererTools.spaceSeperateStrings("inline-edit popover-source", styleClass); - writeAttribute("class", styleClassValue, context); - - writeAttributeIfExists("style", "style", context); - writeStandardAttributes(context); - - // Popover attributes for JS initialization - writeAttribute("data-bs-dual-toggle", "popover", context); - writeAttributeIfExistsOrDefault("title", "data-bs-title", "test title", context); - writeAttributeIfExistsOrDefault("placement", "data-bs-placement", "bottom", context); - writeAttributeIfExistsOrDefault("html", "data-bs-html", "true", context); - writeAttributeIfExistsOrDefault("delay", "data-bs-delay", "{\"show\":500,\"hide\":5000}", context); - writeAttributeIfExistsOrDefault("container", "data-bs-container", "false", context); - - if (getAttributes().get("modal") instanceof UIComponent modalComponent) { - writeAttribute("data-bs-toggle", "modal", context); - writeAttribute("data-bs-target", "#" + modalComponent.getClientId(), context); - } - - // Write label text - String label = (String) getAttributes().getOrDefault("label", "test label"); - writer.writeText(label, "label"); - - // Render children in a hidden template for popover content - writer.startElement("template", this); - writer.writeAttribute("id", getClientId(context) + "_popoverContent", null); - for (UIComponent child : getChildren()) { - child.encodeAll(context); - } - writer.endElement("template"); - - // Reference the template in the popover content attribute - writeAttribute("data-bs-content", "document.getElementById('" + getClientId(context) + "_popoverContent').innerHTML", context); - } - } - - @Override - public void encodeChildren(FacesContext context) { - // Since Children are rendered manually in the encodeBegin we don't want to render them twice - } - - - @Override - public void encodeEnd(FacesContext context) throws IOException { - final boolean editable = getAttribute("editable", true); - final ResponseWriter writer = context.getResponseWriter(); - if (editable) { - writer.endElement("span"); - } - } -} diff --git a/src/main/java/co/cfly/faces/components/PopoverContainerComponent.java b/src/main/java/co/cfly/faces/components/PopoverContainerComponent.java new file mode 100644 index 0000000..461cb97 --- /dev/null +++ b/src/main/java/co/cfly/faces/components/PopoverContainerComponent.java @@ -0,0 +1,75 @@ +package co.cfly.faces.components; + +import java.io.IOException; + +import co.cfly.faces.utils.RendererTools; +import jakarta.faces.component.FacesComponent; +import jakarta.faces.component.UIComponent; +import jakarta.faces.context.FacesContext; +import jakarta.faces.context.ResponseWriter; + +@FacesComponent(value = "co.cfly.faces.components.PopoverContainerComponent", namespace = Families.NAMESPACE) +public class PopoverContainerComponent extends ComponentBase { + + @Override + public String getFamily() { + return Families.OUTPUT_COMPONENT_FAMILY; + } + + @Override + public boolean getRendersChildren() { + return true; + } + + @Override + public void encodeBegin(FacesContext context) throws IOException { + final ResponseWriter writer = context.getResponseWriter(); + writer.startElement("span", this); + writeId(context); + + String styleClass = (String) getAttributes().getOrDefault("styleClass", ""); + String styleClassValue = RendererTools.spaceSeperateStrings("inline-edit popover-source", styleClass); + writeAttribute("class", styleClassValue, context); + + writeAttributeIfExists("style", "style", context); + writeStandardAttributes(context); + + // Popover attributes for JS initialization + writeAttribute("data-bs-dual-toggle", "popover", context); + writeAttributeIfExistsOrDefault("title", "data-bs-title", "test title", context); + writeAttribute("data-bs-placement", "bottom", context); + writeAttribute("data-bs-html", "true", context); + writeAttribute("data-bs-delay", "{\"show\":500,\"hide\":5000}", context); + writeAttribute("data-bs-container", "false", context); + + // Write label text + String label = (String) getAttributes().getOrDefault("label", "test label"); + writer.writeText(label, "label"); + + // Render children in a hidden template for popover content + writer.startElement("template", this); + writer.writeAttribute("id", getClientId(context) + "_popoverContent", null); + for (UIComponent child : getChildren()) { + child.encodeAll(context); + } + writer.endElement("template"); + + // Reference the template in the popover content attribute + writeAttribute("data-bs-content", "document.getElementById('" + getClientId(context) + "_popoverContent').innerHTML", context); + } + + @Override + public void encodeChildren(FacesContext context) { + // Since Children are rendered manually in the encodeBegin we don't want to render them twice + } + + + @Override + public void encodeEnd(FacesContext context) throws IOException { + final boolean editable = getAttribute("editable", true); + final ResponseWriter writer = context.getResponseWriter(); + if (editable) { + writer.endElement("span"); + } + } +} diff --git a/src/main/resources/META-INF/faces.taglib.xml b/src/main/resources/META-INF/faces.taglib.xml index 551f69c..0467c16 100644 --- a/src/main/resources/META-INF/faces.taglib.xml +++ b/src/main/resources/META-INF/faces.taglib.xml @@ -586,14 +586,10 @@ - inlineEditLink + popoverContainer - co.cfly.faces.components.InlineEditLinkComponent + co.cfly.faces.components.PopoverContainerComponent - - editable - boolean - CSS Style style @@ -604,32 +600,6 @@ styleClass java.lang.String - - Modal Component which will be toggled when clicked - modal - true - jakarta.faces.component.UIComponent - - - placement - java.lang.String - - - trigger - java.lang.String - - - html - java.lang.String - - - delay - java.lang.String - - - container - java.lang.String - label java.lang.String