Skip to content

Commit a736aeb

Browse files
authored
feat: add download handler to html components (#21338)
Adds a download handler callback to those flow html components that fulfil data download from server. Replaces the stream resource API. Part of #21255
1 parent d2f56f0 commit a736aeb

File tree

7 files changed

+246
-0
lines changed

7 files changed

+246
-0
lines changed

flow-html-components/src/main/java/com/vaadin/flow/component/html/Anchor.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import com.vaadin.flow.component.PropertyDescriptors;
2828
import com.vaadin.flow.component.Tag;
2929
import com.vaadin.flow.server.AbstractStreamResource;
30+
import com.vaadin.flow.server.DownloadHandler;
31+
import com.vaadin.flow.server.ElementRequestHandler;
3032
import com.vaadin.flow.server.StreamResource;
3133
import com.vaadin.flow.server.StreamResourceRegistry;
3234

@@ -110,6 +112,24 @@ public Anchor(AbstractStreamResource href, String text) {
110112
setText(text);
111113
}
112114

115+
/**
116+
* Creates an anchor component with the given text content and a callback
117+
* that handles data download from the server to the client when clicking an
118+
* anchor.
119+
*
120+
* @see #setHref(DownloadHandler)
121+
* @see #setText(String)
122+
*
123+
* @param downloadHandler
124+
* the callback that handles data download, not null
125+
* @param text
126+
* the text content to set
127+
*/
128+
public Anchor(DownloadHandler downloadHandler, String text) {
129+
setHref(downloadHandler);
130+
setText(text);
131+
}
132+
113133
/**
114134
* Creates an anchor component with the given href and components as
115135
* children of this component.
@@ -174,6 +194,21 @@ public void setHref(AbstractStreamResource href) {
174194
assignHrefAttribute();
175195
}
176196

197+
/**
198+
* Sets the URL that this anchor links to and that is bound to a given
199+
* {@link DownloadHandler} callback on the server for handling data download
200+
* from the server to the client when clicking an anchor.
201+
*
202+
* @param downloadHandler
203+
* the callback that handles data download, not null
204+
*/
205+
public void setHref(DownloadHandler downloadHandler) {
206+
this.href = new StreamResourceRegistry.ElementStreamResource(
207+
downloadHandler, this.getElement());
208+
setRouterIgnore(true);
209+
assignHrefAttribute();
210+
}
211+
177212
/**
178213
* The routing mechanism in Vaadin by default intercepts all anchor elements
179214
* with relative URL. This method can be used make the router ignore this

flow-html-components/src/main/java/com/vaadin/flow/component/html/HtmlObject.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
import com.vaadin.flow.component.PropertyDescriptors;
2727
import com.vaadin.flow.component.Tag;
2828
import com.vaadin.flow.server.AbstractStreamResource;
29+
import com.vaadin.flow.server.DownloadHandler;
2930
import com.vaadin.flow.server.StreamResource;
31+
import com.vaadin.flow.server.StreamResourceRegistry;
3032

3133
/**
3234
* Component representing a <code>&lt;object&gt;</code> element.
@@ -131,6 +133,47 @@ public HtmlObject(AbstractStreamResource data, String type,
131133
add(params);
132134
}
133135

136+
/**
137+
* Creates a new <code>&lt;object&gt;</code> component with given
138+
* {@link DownloadHandler} callback for providing an object data and type
139+
* value.
140+
*
141+
* @see #setData(AbstractStreamResource)
142+
* @see #setType(String)
143+
*
144+
* @param data
145+
* the callback for providing resource data, not null
146+
* @param type
147+
* a type attribute value
148+
*/
149+
public HtmlObject(DownloadHandler data, String type) {
150+
setData(data);
151+
setType(type);
152+
}
153+
154+
/**
155+
* Creates a new <code>&lt;object&gt;</code> component with given data
156+
* resource, type value and "param" components.
157+
*
158+
* @see #setData(String)
159+
* @see #setType(String)
160+
* @see #add(Component...)
161+
*
162+
*
163+
* @param data
164+
* a data attribute value
165+
* @param type
166+
* a type attribute value
167+
* @param params
168+
* parameter components
169+
*/
170+
public HtmlObject(DownloadHandler data, String type, Param... params) {
171+
setData(new StreamResourceRegistry.ElementStreamResource(data,
172+
this.getElement()));
173+
setType(type);
174+
add(params);
175+
}
176+
134177
/**
135178
* Creates a new <code>&lt;object&gt;</code> component with given data and
136179
* "param" components.
@@ -199,6 +242,19 @@ public void setData(AbstractStreamResource data) {
199242
getElement().setAttribute("data", data);
200243
}
201244

245+
/**
246+
* Sets the URL for {@link DownloadHandler} callback as "data" attribute
247+
* value .
248+
*
249+
* @param data
250+
* a "data" attribute value,, not {@code null}
251+
*/
252+
public void setData(DownloadHandler data) {
253+
getElement().setAttribute("data",
254+
new StreamResourceRegistry.ElementStreamResource(data,
255+
this.getElement()));
256+
}
257+
202258
/**
203259
* Gets the "data" attribute value.
204260
*

flow-html-components/src/main/java/com/vaadin/flow/component/html/IFrame.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
import com.vaadin.flow.component.PropertyDescriptors;
2222
import com.vaadin.flow.component.Tag;
2323
import com.vaadin.flow.server.AbstractStreamResource;
24+
import com.vaadin.flow.server.DownloadHandler;
2425
import com.vaadin.flow.server.StreamResource;
26+
import com.vaadin.flow.server.StreamResourceRegistry;
2527

2628
import java.util.Optional;
2729
import java.util.stream.Collectors;
@@ -134,6 +136,18 @@ public IFrame(String src) {
134136
setSrc(src);
135137
}
136138

139+
/**
140+
* Creates a new iframe with download handler callback that provides a
141+
* resource from server.
142+
*
143+
* @param downloadHandler
144+
* the download handler callback that provides a resource from
145+
* server, not null
146+
*/
147+
public IFrame(DownloadHandler downloadHandler) {
148+
setSrc(downloadHandler);
149+
}
150+
137151
/**
138152
* Sets the source of the iframe. If the contents at the src of the IFrame
139153
* has changed and you want to refresh it in the user's browser, the src
@@ -159,6 +173,21 @@ public void setSrc(AbstractStreamResource src) {
159173
getElement().setAttribute("src", src);
160174
}
161175

176+
/**
177+
* Sets the source of the iframe with a source URL with the URL of the given
178+
* {@link DownloadHandler} callback.
179+
*
180+
* @see #setSrc(String)
181+
*
182+
* @param downloadHandler
183+
* the download handler resource, not null
184+
*/
185+
public void setSrc(DownloadHandler downloadHandler) {
186+
getElement().setAttribute("src",
187+
new StreamResourceRegistry.ElementStreamResource(
188+
downloadHandler, this.getElement()));
189+
}
190+
162191
/**
163192
* Gets the source of the iframe.
164193
*

flow-html-components/src/main/java/com/vaadin/flow/component/html/Image.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
import com.vaadin.flow.component.PropertyDescriptors;
2525
import com.vaadin.flow.component.Tag;
2626
import com.vaadin.flow.server.AbstractStreamResource;
27+
import com.vaadin.flow.server.DownloadHandler;
2728
import com.vaadin.flow.server.StreamResource;
29+
import com.vaadin.flow.server.StreamResourceRegistry;
2830

2931
/**
3032
* Component representing a <code>&lt;img&gt;</code> element.
@@ -85,6 +87,27 @@ public Image(AbstractStreamResource src, String alt) {
8587
setAlt(alt);
8688
}
8789

90+
/**
91+
* Creates an image with the given download handler callback for providing
92+
* an image data and an alternative text.
93+
* <p>
94+
* The alternative text given to constructor is always set even if it is the
95+
* default empty string which is not retained with {@link #setAlt(String)}.
96+
*
97+
* @param downloadHandler
98+
* the download handler callback that provides an image data, not
99+
* null
100+
* @param alt
101+
* the alternate text
102+
*
103+
* @see #setSrc(AbstractStreamResource)
104+
* @see #setAlt(String)
105+
*/
106+
public Image(DownloadHandler downloadHandler, String alt) {
107+
setSrc(downloadHandler);
108+
setAlt(alt);
109+
}
110+
88111
/**
89112
* Gets the image URL.
90113
*
@@ -114,6 +137,19 @@ public void setSrc(AbstractStreamResource src) {
114137
getElement().setAttribute("src", src);
115138
}
116139

140+
/**
141+
* Sets the image URL with the URL of the given {@link DownloadHandler}
142+
* callback.
143+
*
144+
* @param downloadHandler
145+
* the download handler resource, not null
146+
*/
147+
public void setSrc(DownloadHandler downloadHandler) {
148+
getElement().setAttribute("src",
149+
new StreamResourceRegistry.ElementStreamResource(
150+
downloadHandler, this.getElement()));
151+
}
152+
117153
/**
118154
* Sets the alternate text for the image.
119155
*

flow-html-components/src/test/java/com/vaadin/flow/component/html/AnchorTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.vaadin.flow.component.Text;
2525
import com.vaadin.flow.component.UI;
2626
import com.vaadin.flow.server.AbstractStreamResource;
27+
import com.vaadin.flow.server.DownloadHandler;
2728

2829
public class AnchorTest extends ComponentTest {
2930

@@ -270,6 +271,45 @@ public String getName() {
270271
Assert.assertNotEquals(href, anchor.getHref());
271272
}
272273

274+
@Test
275+
public void disabledAnchor_setDownload_hrefIsRemoved_enableAnchor_hrefIsRestored() {
276+
mockUI();
277+
DownloadHandler downloadHandler = event -> event.getWriter()
278+
.write("foo");
279+
Anchor anchor = new Anchor(downloadHandler, "bar");
280+
String href = anchor.getHref();
281+
anchor.setEnabled(false);
282+
283+
Assert.assertFalse(anchor.getElement().hasAttribute("href"));
284+
Assert.assertEquals(href, anchor.getHref());
285+
286+
anchor.setEnabled(true);
287+
Assert.assertEquals(href, anchor.getHref());
288+
}
289+
290+
@Test
291+
public void disabledAnchor_setDownloadWhenDisabled_hrefIsPreserved() {
292+
mockUI();
293+
DownloadHandler downloadHandler = event -> event.getWriter()
294+
.write("foo");
295+
Anchor anchor = new Anchor(downloadHandler, "bar");
296+
String href = anchor.getHref();
297+
anchor.setEnabled(false);
298+
299+
anchor.setHref(new AbstractStreamResource() {
300+
301+
@Override
302+
public String getName() {
303+
return "baz";
304+
}
305+
});
306+
307+
anchor.setEnabled(true);
308+
309+
Assert.assertTrue(anchor.getElement().hasAttribute("href"));
310+
Assert.assertNotEquals(href, anchor.getHref());
311+
}
312+
273313
private void mockUI() {
274314
ui = new UI();
275315
UI.setCurrent(ui);

flow-html-components/src/test/java/com/vaadin/flow/component/html/HtmlComponentSmokeTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import com.vaadin.flow.internal.StateNode;
5050
import com.vaadin.flow.internal.change.NodeChange;
5151
import com.vaadin.flow.server.AbstractStreamResource;
52+
import com.vaadin.flow.server.DownloadHandler;
5253

5354
public class HtmlComponentSmokeTest {
5455

@@ -248,6 +249,29 @@ private static boolean isSpecialSetter(Method method) {
248249
return true;
249250
}
250251

252+
if (method.getDeclaringClass() == IFrame.class
253+
&& method.getName().startsWith("setSrc")) {
254+
return true;
255+
}
256+
257+
if (method.getDeclaringClass() == HtmlObject.class
258+
&& method.getName().startsWith("setData")
259+
&& method.getParameterTypes()[0] == DownloadHandler.class) {
260+
return true;
261+
}
262+
263+
if (method.getDeclaringClass() == Anchor.class
264+
&& method.getName().startsWith("setHref")
265+
&& method.getParameterTypes()[0] == DownloadHandler.class) {
266+
return true;
267+
}
268+
269+
if (method.getDeclaringClass() == Image.class
270+
&& method.getName().startsWith("setSrc")
271+
&& method.getParameterTypes()[0] == DownloadHandler.class) {
272+
return true;
273+
}
274+
251275
return false;
252276
}
253277

flow-html-components/src/test/java/com/vaadin/flow/component/html/HtmlObjectTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package com.vaadin.flow.component.html;
1717

18+
import java.io.IOException;
1819
import java.net.URI;
1920

2021
import org.junit.After;
@@ -76,4 +77,29 @@ public void setData_dataAsAResourceinCTOR() {
7677
Assert.assertEquals(uri.toASCIIString(),
7778
object.getElement().getAttribute("data"));
7879
}
80+
81+
@Test
82+
public void setDownloadHandlerData_dataAsAResource() {
83+
UI ui = new UI();
84+
UI.setCurrent(ui);
85+
HtmlObject object = new HtmlObject();
86+
object.setData(event -> event.getWriter().write("foo"));
87+
88+
Assert.assertTrue("Data should be set as dynamic resource.",
89+
object.getElement().getAttribute("data")
90+
.startsWith("VAADIN/dynamic/resource/-1/"));
91+
}
92+
93+
@Test
94+
public void setDownloadHandlerData_dataAsAResourceinCTOR() {
95+
UI ui = new UI();
96+
UI.setCurrent(ui);
97+
98+
HtmlObject object = new HtmlObject(
99+
event -> event.getWriter().write("foo"), "foo");
100+
101+
Assert.assertTrue("Data should be set as dynamic resource.",
102+
object.getElement().getAttribute("data")
103+
.startsWith("VAADIN/dynamic/resource/-1/"));
104+
}
79105
}

0 commit comments

Comments
 (0)