Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Proper handling of href when the anchor is disabled/enabled (#12616) (#12633) (CP: 2.7) #12639

Merged
merged 1 commit into from
Dec 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.vaadin.flow.component.html;

import java.io.Serializable;
import java.util.Objects;
import java.util.Optional;

Expand All @@ -26,6 +27,7 @@
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.server.AbstractStreamResource;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.server.StreamResourceRegistry;

/**
* Component representing an <code>&lt;a&gt;</code> element.
Expand All @@ -43,6 +45,9 @@ public class Anchor extends HtmlContainer implements Focusable<Anchor> {
.optionalAttributeWithDefault("target",
AnchorTarget.DEFAULT.getValue());

private static final String ROUTER_IGNORE_ATTRIBUTE = "router-ignore";
private Serializable href;

/**
* Creates a new empty anchor component.
*/
Expand Down Expand Up @@ -123,6 +128,9 @@ public Anchor(String href, Component... components) {
/**
* Sets the URL that this anchor links to.
* <p>
* A disabled Anchor removes the attribute from the HTML element, but it is
* stored (and reused when enabled again) in the server-side component.
* <p>
* Use the method {@link #removeHref()} to remove the <b>href</b> attribute
* instead of setting it to an empty string.
*
Expand All @@ -133,7 +141,11 @@ public Anchor(String href, Component... components) {
* the href to set
*/
public void setHref(String href) {
set(hrefDescriptor, href);
if (href == null) {
throw new IllegalArgumentException("Href must not be null");
}
this.href = href;
assignHrefAttribute();
}

/**
Expand All @@ -144,6 +156,7 @@ public void setHref(String href) {
*/
public void removeHref() {
getElement().removeAttribute("href");
href = null;
}

/**
Expand All @@ -154,7 +167,9 @@ public void removeHref() {
* the resource value, not null
*/
public void setHref(AbstractStreamResource href) {
getElement().setAttribute("href", href);
this.href = href;
getElement().setAttribute(ROUTER_IGNORE_ATTRIBUTE, true);
assignHrefAttribute();
}

/**
Expand All @@ -165,9 +180,37 @@ public void setHref(AbstractStreamResource href) {
* @return the href value, or <code>""</code> if no href has been set
*/
public String getHref() {
if (href instanceof String) {
// let the method return the actual href string even if disabled
return (String) href;
} else if (href instanceof AbstractStreamResource) {
return StreamResourceRegistry.getURI((AbstractStreamResource) href)
.toString();
}
return get(hrefDescriptor);
}

@Override
public void onEnabledStateChanged(boolean enabled) {
super.onEnabledStateChanged(enabled);
assignHrefAttribute();
}

private void assignHrefAttribute() {
if (isEnabled()) {
if (href != null) {
if (href instanceof AbstractStreamResource) {
getElement().setAttribute("href",
(AbstractStreamResource) href);
} else {
set(hrefDescriptor, (String) href);
}
}
} else {
getElement().removeAttribute("href");
}
}

/**
* Sets the target window, tab or frame for this anchor. The target is
* either the <code>window.name</code> of a specific target, or one of these
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,24 @@

import java.util.Optional;

import com.vaadin.flow.component.UI;
import com.vaadin.flow.server.AbstractStreamResource;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;

import com.vaadin.flow.component.Text;

public class AnchorTest extends ComponentTest {

private UI ui;

@After
public void tearDown() {
ui = null;
UI.setCurrent(null);
}

@Test
public void removeHref() {
Anchor anchor = new Anchor();
Expand Down Expand Up @@ -107,4 +118,130 @@ protected void addProperties() {
addOptionalStringProperty("target");
}

@Test
public void setEnabled_anchorWithoutHref_doesNotThrow() {
Anchor anchor = new Anchor();
anchor.setEnabled(false);

anchor.setEnabled(true);
Assert.assertTrue(anchor.isEnabled());

anchor.setHref("foo");
anchor.setEnabled(false);
anchor.removeHref();
anchor.setEnabled(true);
}

@Test
public void disabledAnchor_removeHref_hrefIsEmpty() {
Anchor anchor = new Anchor();
anchor.setHref("foo");
anchor.setEnabled(false);
Assert.assertEquals("foo", anchor.getHref());
anchor.setHref("bar");
Assert.assertEquals("bar", anchor.getHref());
anchor.removeHref();
Assert.assertEquals("", anchor.getHref());
anchor.setEnabled(true);
Assert.assertEquals("", anchor.getHref());
}

@Test
public void disabledAnchor_hrefIsRemoved_enableAnchor_hrefIsRestored() {
Anchor anchor = new Anchor("foo", "bar");
anchor.setEnabled(false);

Assert.assertFalse(anchor.getElement().hasAttribute("href"));

anchor.setEnabled(true);
Assert.assertTrue(anchor.getElement().hasAttribute("href"));
Assert.assertEquals("foo", anchor.getHref());
}

@Test
public void disabledAnchor_setHrefWhenDisabled_enableAnchor_hrefIsPreserved() {
Anchor anchor = new Anchor("foo", "bar");
anchor.setEnabled(false);

anchor.setHref("baz");

anchor.setEnabled(true);

Assert.assertTrue(anchor.getElement().hasAttribute("href"));
Assert.assertEquals("baz", anchor.getHref());
}

@Test
public void disabledAnchor_setResourceWhenDisabled_enableAnchor_resourceIsPreserved() {
Anchor anchor = new Anchor("foo", "bar");
anchor.setEnabled(false);

mockUI();
anchor.setHref(new AbstractStreamResource() {

@Override
public String getName() {
return "baz";
}
});
anchor.setEnabled(true);

Assert.assertTrue(anchor.getElement().hasAttribute("href"));
}

@Test
public void disabledAnchor_setResource_hrefIsRemoved_enableAnchor_hrefIsRestored() {
mockUI();
AbstractStreamResource resource = new AbstractStreamResource() {

@Override
public String getName() {
return "foo";
}

};
Anchor anchor = new Anchor(resource, "bar");
String href = anchor.getHref();
anchor.setEnabled(false);

Assert.assertFalse(anchor.getElement().hasAttribute("href"));
Assert.assertEquals(href, anchor.getHref());

anchor.setEnabled(true);
Assert.assertEquals(href, anchor.getHref());
}

@Test
public void disabledAnchor_setResourceWhenDisabled_hrefIsPreserved() {
mockUI();
AbstractStreamResource resource = new AbstractStreamResource() {

@Override
public String getName() {
return "foo";
}

};
Anchor anchor = new Anchor(resource, "bar");
String href = anchor.getHref();
anchor.setEnabled(false);

anchor.setHref(new AbstractStreamResource() {

@Override
public String getName() {
return "baz";
}
});

anchor.setEnabled(true);

Assert.assertTrue(anchor.getElement().hasAttribute("href"));
Assert.assertNotEquals(href, anchor.getHref());
}

private void mockUI() {
ui = new UI();
UI.setCurrent(ui);
}
}