diff --git a/flow-html-components/src/main/java/com/vaadin/flow/component/html/Anchor.java b/flow-html-components/src/main/java/com/vaadin/flow/component/html/Anchor.java index 19f6ae8d168..18cb2b7a785 100644 --- a/flow-html-components/src/main/java/com/vaadin/flow/component/html/Anchor.java +++ b/flow-html-components/src/main/java/com/vaadin/flow/component/html/Anchor.java @@ -15,6 +15,7 @@ */ package com.vaadin.flow.component.html; +import java.util.Objects; import java.util.Optional; import com.vaadin.flow.component.Component; @@ -39,7 +40,8 @@ public class Anchor extends HtmlContainer implements Focusable { .attributeWithDefault("href", "", false); private static final PropertyDescriptor> targetDescriptor = PropertyDescriptors - .optionalAttributeWithDefault("target", ""); + .optionalAttributeWithDefault("target", + AnchorTarget.DEFAULT.getValue()); private static final String ROUTER_IGNORE_ATTRIBUTE = "router-ignore"; @@ -66,6 +68,26 @@ public Anchor(String href, String text) { setText(text); } + /** + * Creates an anchor component with the given target, text content and href. + * + * @see #setHref(String) + * @see #setText(String) + * @see #setTarget(AnchorTargetValue) + * + * @param href + * the href to set + * @param text + * the text content to set + * @param target + * the target window, tab or frame + */ + public Anchor(String href, String text, AnchorTarget target) { + setHref(href); + setText(text); + setTarget(target); + } + /** * Creates an anchor component with the given text content and stream * resource. @@ -183,4 +205,47 @@ public Optional getTarget() { return get(targetDescriptor); } + /** + * Sets the target window, tab or frame for this anchor. The target may be + * the one of these special values: + *
    + *
  • AnchorTarget.DEFAULT: Removes the target value. This has + * the same effect as setting the target to AnchorTarget.SELF. + *
  • AnchorTarget.SELF: Opens the link in the current + * context. + *
  • AnchorTarget.BLANK: Opens the link in a new unnamed + * context. + *
  • AnchorTarget.PARENT: Opens the link in the parent + * context, or the current context if there is no parent context. + *
  • AnchorTarget.TOP: Opens the link in the top most + * grandparent context, or the current context if there is no parent + * context. + *
+ * + * @param target + * the target value, not null + */ + public void setTarget(AnchorTargetValue target) { + Objects.requireNonNull(target, "target cannot be null."); + setTarget(target.getValue()); + } + + /** + * Gets the target window, tab or frame value for this anchor. + * + * @see #setTarget(AnchorTargetValue) + * @see #getTarget() + * + * @return the target window value , or {@link AnchorTarget#DEFAULT} if no + * target has been set + */ + public AnchorTargetValue getTargetValue() { + Optional target = getTarget(); + + if (target.isPresent()) { + return AnchorTargetValue.forString(target.get()); + } + return AnchorTarget.DEFAULT; + } + } diff --git a/flow-html-components/src/main/java/com/vaadin/flow/component/html/AnchorTarget.java b/flow-html-components/src/main/java/com/vaadin/flow/component/html/AnchorTarget.java new file mode 100644 index 00000000000..a6efab58081 --- /dev/null +++ b/flow-html-components/src/main/java/com/vaadin/flow/component/html/AnchorTarget.java @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2020 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.html; + +/** + * Enum representing target attribute values for an + * <a> element. + * + * @author Vaadin Ltd + * @since + */ +public enum AnchorTarget implements AnchorTargetValue { + /** + * Remove the target value. This has the same effect as SELF. + */ + DEFAULT(""), + /** + * Open a link in the current context. + */ + SELF("_self"), + /** + * Open a link in a new unnamed context. + */ + BLANK("_blank"), + /** + * Open a link in the parent context, or the current context if there is no + * parent context. + */ + PARENT("_parent"), + /** + * Open a link in the top most grandparent context, or the current context + * if there is no parent context. + */ + TOP("_top"); + + private final String value; + + /** + * @param value + * the text value to use by an {@code } (anchor) tag. + */ + AnchorTarget(String value) { + this.value = value; + } + + /** + * @return value the text value to use by an {@code } (anchor) tag. + */ + @Override + public String getValue() { + return value; + } + +} diff --git a/flow-html-components/src/main/java/com/vaadin/flow/component/html/AnchorTargetValue.java b/flow-html-components/src/main/java/com/vaadin/flow/component/html/AnchorTargetValue.java new file mode 100644 index 00000000000..3d406b1cf5b --- /dev/null +++ b/flow-html-components/src/main/java/com/vaadin/flow/component/html/AnchorTargetValue.java @@ -0,0 +1,84 @@ +/* + * Copyright 2000-2021 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.html; + +import java.io.Serializable; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +/** + * Represents target attribute values for an <a> + * element. + * + * @author Vaadin Ltd + * @since + * + * @see AnchorTarget + * + */ +@FunctionalInterface +public interface AnchorTargetValue extends Serializable { + + /** + * Gets the string value representation. + * + * @return string value representation + */ + String getValue(); + + /** + * Gets an object instance wrapping the {@code value} string representation. + * + * @param value + * the string value representation, not {@code null} + * @return an object wrapping the string value + */ + public static AnchorTargetValue forString(String value) { + Optional target = Stream.of(AnchorTarget.values()).filter( + type -> type.getValue().equals(Objects.requireNonNull(value))) + .findFirst(); + if (target.isPresent()) { + return target.get(); + } + return new AnchorTargetValue() { + + @Override + public String getValue() { + return value; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj == this) { + return true; + } + if (!obj.getClass().equals(getClass())) { + return false; + } + return value.equals(((AnchorTargetValue) obj).getValue()); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + }; + } +} diff --git a/flow-html-components/src/test/java/com/vaadin/flow/component/html/AnchorTargetValueTest.java b/flow-html-components/src/test/java/com/vaadin/flow/component/html/AnchorTargetValueTest.java new file mode 100644 index 00000000000..83ef9b198f0 --- /dev/null +++ b/flow-html-components/src/test/java/com/vaadin/flow/component/html/AnchorTargetValueTest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2021 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.html; + +import org.junit.Assert; +import org.junit.Test; + +public class AnchorTargetValueTest { + + @Test + public void fromString_notEnum_objectHasValueAndEquals() { + AnchorTargetValue value = AnchorTargetValue.forString("foo"); + Assert.assertEquals("foo", value.getValue()); + + AnchorTargetValue value1 = AnchorTargetValue.forString("foo"); + Assert.assertEquals(value, value1); + Assert.assertEquals(value.hashCode(), value1.hashCode()); + } + + @Test + public void fromString_enumValue_resultIsEnum() { + AnchorTargetValue value = AnchorTargetValue + .forString(AnchorTarget.TOP.getValue()); + Assert.assertEquals(AnchorTarget.TOP, value); + } +} diff --git a/flow-html-components/src/test/java/com/vaadin/flow/component/html/AnchorTest.java b/flow-html-components/src/test/java/com/vaadin/flow/component/html/AnchorTest.java index 3d5b068fb6f..ed04899aa9b 100644 --- a/flow-html-components/src/test/java/com/vaadin/flow/component/html/AnchorTest.java +++ b/flow-html-components/src/test/java/com/vaadin/flow/component/html/AnchorTest.java @@ -15,10 +15,13 @@ */ package com.vaadin.flow.component.html; -import com.vaadin.flow.component.Text; +import java.util.Optional; + import org.junit.Assert; import org.junit.Test; +import com.vaadin.flow.component.Text; + public class AnchorTest extends ComponentTest { @Test @@ -35,7 +38,27 @@ public void removeHref() { public void createWithComponent() { Anchor anchor = new Anchor("#", new Text("Home")); Assert.assertEquals(anchor.getElement().getAttribute("href"), "#"); + Assert.assertEquals(anchor.getHref(), "#"); Assert.assertEquals(anchor.getElement().getText(), "Home"); + Assert.assertEquals(anchor.getText(), "Home"); + } + + @Test + public void createWithTarget() { + Anchor anchor = new Anchor("#", "Home"); + Assert.assertEquals(anchor.getTargetValue(), AnchorTarget.DEFAULT); + Assert.assertEquals(anchor.getTarget(), Optional.empty()); + + anchor.setTarget(AnchorTarget.BLANK); + + Assert.assertEquals(anchor.getTargetValue(), AnchorTarget.BLANK); + Assert.assertEquals(anchor.getTarget(), + Optional.of(AnchorTarget.BLANK.getValue())); + + Assert.assertEquals(anchor.getTargetValue(), + new Anchor("#", "Home", AnchorTarget.BLANK).getTargetValue()); + Assert.assertEquals(anchor.getTarget(), + new Anchor("#", "Home", AnchorTarget.BLANK).getTarget()); } @Test @@ -60,6 +83,44 @@ public void shouldNotBreakBehaviorIfSetHrefWhenHavingRouterIgnoreAttributeBefore anchor.getElement().getAttribute("router-ignore")); } + @Test + public void setTargetValue_useEnum_targetIsSet() { + Anchor anchor = new Anchor(); + anchor.setTarget(AnchorTarget.PARENT); + + Assert.assertEquals(Optional.of(AnchorTarget.PARENT.getValue()), + anchor.getTarget()); + Assert.assertEquals(AnchorTarget.PARENT, anchor.getTargetValue()); + } + + @Test + public void setTargetValue_useObject_targetIsSet() { + Anchor anchor = new Anchor(); + anchor.setTarget(AnchorTargetValue.forString("foo")); + + Assert.assertEquals(Optional.of("foo"), anchor.getTarget()); + Assert.assertEquals("foo", anchor.getTargetValue().getValue()); + } + + @Test + public void getTargetValue_useEnumStringValue_targetIsReturned() { + Anchor anchor = new Anchor(); + anchor.setTarget(AnchorTarget.SELF.getValue()); + + Assert.assertEquals(Optional.of(AnchorTarget.SELF.getValue()), + anchor.getTarget()); + Assert.assertEquals(AnchorTarget.SELF, anchor.getTargetValue()); + } + + @Test + public void getTargetValue_useSomeStringValue_targetIsReturned() { + Anchor anchor = new Anchor(); + anchor.setTarget("foo"); + + Assert.assertEquals(Optional.of("foo"), anchor.getTarget()); + Assert.assertEquals("foo", anchor.getTargetValue().getValue()); + } + // Other test methods in super class @Override diff --git a/flow-html-components/src/test/java/com/vaadin/flow/component/html/HtmlComponentSmokeTest.java b/flow-html-components/src/test/java/com/vaadin/flow/component/html/HtmlComponentSmokeTest.java index 85c03d799ac..ac361d940c1 100644 --- a/flow-html-components/src/test/java/com/vaadin/flow/component/html/HtmlComponentSmokeTest.java +++ b/flow-html-components/src/test/java/com/vaadin/flow/component/html/HtmlComponentSmokeTest.java @@ -26,7 +26,14 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.function.Supplier; import org.junit.Assert; @@ -197,6 +204,15 @@ private static boolean isSpecialSetter(Method method) { && method.getParameterTypes()[0] == Component.class) { return true; } + + // Anchor.setTarget(AnchorTargetValue) - + // https://github.com/vaadin/flow/issues/8346 + if (method.getDeclaringClass() == Anchor.class + && method.getName().equals("setTarget") + && method.getParameterTypes()[0] == AnchorTargetValue.class) { + return true; + } + // setFoo(AbstractStreamResource) for resource URLs if (method.getParameterCount() == 1 && AbstractStreamResource.class .isAssignableFrom(method.getParameters()[0].getType())) {