• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

knowledgepixels / nanodash / 21590827488

02 Feb 2026 12:51PM UTC coverage: 13.774% (-0.2%) from 13.94%
21590827488

push

github

tkuhn
feat: Add filter fields to views to User and Query pages

545 of 5252 branches covered (10.38%)

Branch coverage included in aggregate %.

1492 of 9537 relevant lines covered (15.64%)

2.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
src/main/java/com/knowledgepixels/nanodash/component/ItemListPanel.java
1
package com.knowledgepixels.nanodash.component;
2

3
import java.io.Serializable;
4
import java.util.ArrayList;
5
import java.util.List;
6
import java.util.function.Function;
7
import java.util.function.Supplier;
8

9
import com.knowledgepixels.nanodash.FilteredListDataProvider;
10

11
import org.apache.wicket.Component;
12
import org.apache.wicket.ajax.AjaxRequestTarget;
13
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
14
import org.apache.wicket.behavior.AttributeAppender;
15
import org.apache.wicket.extensions.ajax.markup.html.AjaxLazyLoadPanel;
16
import org.apache.wicket.markup.html.WebMarkupContainer;
17
import org.apache.wicket.markup.html.basic.Label;
18
import org.apache.wicket.markup.html.form.TextField;
19
import org.apache.wicket.markup.html.link.AbstractLink;
20
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
21
import org.apache.wicket.markup.html.panel.Panel;
22
import org.apache.wicket.model.IModel;
23
import org.apache.wicket.model.Model;
24
import org.apache.wicket.request.mapper.parameter.PageParameters;
25
import org.nanopub.extra.services.ApiResponse;
26
import org.nanopub.extra.services.QueryRef;
27

28
import com.knowledgepixels.nanodash.ApiCache;
29
import com.knowledgepixels.nanodash.ProfiledResource;
30
import com.knowledgepixels.nanodash.page.NanodashPage;
31

32
public class ItemListPanel<T extends Serializable> extends Panel {
33

34
    private String description;
35
    private List<AbstractLink> buttons = new ArrayList<>();
×
36
    private List<AbstractLink> memberButtons = new ArrayList<>();
×
37
    private List<AbstractLink> adminButtons = new ArrayList<>();
×
38
    private ProfiledResource profiledResource;
39
    private boolean finalized = false;
×
40
    private boolean lazyLoading = false;
×
41
    private ReadyFunction readyFunction;
42

43
    private ItemListPanel(String markupId, String title) {
44
        super(markupId);
×
45
        setOutputMarkupId(true);
×
46

47
        add(new Label("title", title));
×
48
        WebMarkupContainer filterContainer = new WebMarkupContainer("filterContainer");
×
49
        filterContainer.add(new TextField<>("filter", Model.of("")).setVisible(false));
×
50
        filterContainer.setVisible(false);
×
51
        add(filterContainer);
×
52
    }
×
53

54
    private void addFilterAndItemList(String itemListId, List<T> items, ComponentProvider<T> compProvider, FilteredListDataProvider.SerializableFunction<T, String> filterTextGetter) {
55
        if (filterTextGetter != null) {
×
56
            IModel<String> filterModel = Model.of("");
×
57
            TextField<String> filterField = new TextField<>("filter", filterModel);
×
58
            filterField.setOutputMarkupId(true);
×
59
            filterField.add(new AjaxFormComponentUpdatingBehavior("change") {
×
60
                @Override
61
                protected void onUpdate(AjaxRequestTarget target) {
62
                    target.add(ItemListPanel.this);
×
63
                }
×
64
            });
65
            WebMarkupContainer filterContainer = new WebMarkupContainer("filterContainer");
×
66
            filterContainer.add(filterField);
×
67
            get("filterContainer").replaceWith(filterContainer);
×
68
            add(new ItemList<T>(itemListId, items, compProvider, filterTextGetter, filterModel));
×
69
        } else {
×
70
            add(new ItemList<T>(itemListId, items, compProvider, null));
×
71
        }
72
    }
×
73

74
    public ItemListPanel(String markupId, String title, List<T> items, ComponentProvider<T> compProvider) {
75
        this(markupId, title, items, compProvider, null);
×
76
    }
×
77

78
    public ItemListPanel(String markupId, String title, List<T> items, ComponentProvider<T> compProvider, FilteredListDataProvider.SerializableFunction<T, String> filterTextGetter) {
79
        this(markupId, title);
×
80
        addFilterAndItemList("itemlist", items, compProvider, filterTextGetter);
×
81
    }
×
82

83
    public ItemListPanel(String markupId, String title, QueryRef queryRef, ApiResultListProvider<T> resultListProvider, ComponentProvider<T> compProvider) {
84
        this(markupId, title, queryRef, resultListProvider, compProvider, null);
×
85
    }
×
86

87
    public ItemListPanel(String markupId, String title, QueryRef queryRef, ApiResultListProvider<T> resultListProvider, ComponentProvider<T> compProvider, FilteredListDataProvider.SerializableFunction<T, String> filterTextGetter) {
88
        this(markupId, title);
×
89

90
        ApiResponse qResponse = ApiCache.retrieveResponseAsync(queryRef);
×
91
        if (qResponse != null) {
×
92
            addFilterAndItemList("itemlist", resultListProvider.apply(qResponse), compProvider, filterTextGetter);
×
93
        } else {
94
            lazyLoading = true;
×
95
            final FilteredListDataProvider.SerializableFunction<T, String> getter = filterTextGetter;
×
96
            add(new ApiResultComponent("itemlist", queryRef) {
×
97
                @Override
98
                public Component getApiResultComponent(String markupId, ApiResponse response) {
99
                    if (getter != null) {
×
100
                        IModel<String> filterModel = Model.of("");
×
101
                        ItemListPanel.this.get("filterContainer").replaceWith(createFilterContainer(filterModel, ItemListPanel.this));
×
102
                        return new ItemList<T>(markupId, resultListProvider.apply(response), compProvider, getter, filterModel);
×
103
                    }
104
                    return new ItemList<T>(markupId, resultListProvider.apply(response), compProvider, null);
×
105
                }
106
            });
107
        }
108
    }
×
109

110
    private WebMarkupContainer createFilterContainer(IModel<String> filterModel, ItemListPanel<?> panel) {
111
        WebMarkupContainer filterContainer = new WebMarkupContainer("filterContainer");
×
112
        TextField<String> filterField = new TextField<>("filter", filterModel);
×
113
        filterField.setOutputMarkupId(true);
×
114
        filterField.add(new AjaxFormComponentUpdatingBehavior("change") {
×
115
            @Override
116
            protected void onUpdate(AjaxRequestTarget target) {
117
                target.add(panel);
×
118
            }
×
119
        });
120
        filterContainer.add(filterField);
×
121
        return filterContainer;
×
122
    }
123

124
    public ItemListPanel(String markupId, String title, ReadyFunction readyFunction, ResultFunction<List<T>> resultFunction, ComponentProvider<T> compProvider) {
125
        this(markupId, title, readyFunction, resultFunction, compProvider, null);
×
126
    }
×
127

128
    public ItemListPanel(String markupId, String title, ReadyFunction readyFunction, ResultFunction<List<T>> resultFunction, ComponentProvider<T> compProvider, FilteredListDataProvider.SerializableFunction<T, String> filterTextGetter) {
129
        this(markupId, title);
×
130
        this.readyFunction = readyFunction;
×
131

132
        if (readyFunction.get()) {
×
133
            addFilterAndItemList("itemlist", resultFunction.get(), compProvider, filterTextGetter);
×
134
        } else {
135
            lazyLoading = true;
×
136
            final FilteredListDataProvider.SerializableFunction<T, String> getter = filterTextGetter;
×
137
            add(new MethodResultComponent<List<T>>("itemlist", readyFunction, resultFunction) {
×
138
                @Override
139
                public Component getResultComponent(String markupId, List<T> result) {
140
                    if (getter != null) {
×
141
                        IModel<String> filterModel = Model.of("");
×
142
                        ItemListPanel.this.get("filterContainer").replaceWith(createFilterContainer(filterModel, ItemListPanel.this));
×
143
                        return new ItemList<T>(markupId, resultFunction.get(), compProvider, getter, filterModel);
×
144
                    }
145
                    return new ItemList<T>(markupId, resultFunction.get(), compProvider, null);
×
146
                }
147
            });
148
        }
149
    }
×
150

151
    public ItemListPanel<T> setDescription(String description) {
152
        this.description = description;
×
153
        return this;
×
154
    }
155

156
    public ItemListPanel<T> makeInline() {
157
        add(new AttributeAppender("class", " inline"));
×
158
        return this;
×
159
    }
160

161
    // TODO Improve this (member/admin) button handling:
162
    public ItemListPanel<T> addButton(String label, Class<? extends NanodashPage> pageClass, PageParameters parameters) {
163
        if (parameters == null) parameters = new PageParameters();
×
164
        if (profiledResource != null) parameters.set("context", profiledResource.getId());
×
165
        AbstractLink button = new BookmarkablePageLink<NanodashPage>("button", pageClass, parameters);
×
166
        button.setBody(Model.of(label));
×
167
        buttons.add(button);
×
168
        return this;
×
169
    }
170

171
    public ItemListPanel<T> addMemberButton(String label, Class<? extends NanodashPage> pageClass, PageParameters parameters) {
172
        if (parameters == null) parameters = new PageParameters();
×
173
        if (profiledResource != null) parameters.set("context", profiledResource.getId());
×
174
        AbstractLink button = new BookmarkablePageLink<NanodashPage>("button", pageClass, parameters);
×
175
        button.setBody(Model.of(label));
×
176
        memberButtons.add(button);
×
177
        return this;
×
178
    }
179

180
    public ItemListPanel<T> addAdminButton(String label, Class<? extends NanodashPage> pageClass, PageParameters parameters) {
181
        if (parameters == null) parameters = new PageParameters();
×
182
        if (profiledResource != null) parameters.set("context", profiledResource.getId());
×
183
        AbstractLink button = new BookmarkablePageLink<NanodashPage>("button", pageClass, parameters);
×
184
        button.setBody(Model.of(label));
×
185
        adminButtons.add(button);
×
186
        return this;
×
187
    }
188

189
    public ItemListPanel<T> setProfiledResource(ProfiledResource profiledResource) {
190
        this.profiledResource = profiledResource;
×
191
        return this;
×
192
    }
193

194
    public ItemListPanel<T> setReadyFunction(ReadyFunction readyFunction) {
195
        this.readyFunction = readyFunction;
×
196
        return this;
×
197
    }
198

199
    @Override
200
    protected void onBeforeRender() {
201
        if (!finalized) {
×
202
            add(new Label("description", description).setVisible(description != null));
×
203
            if (profiledResource != null && readyFunction != null && !readyFunction.get()) {
×
204
                add(new AjaxLazyLoadPanel<Component>("buttons") {
×
205

206
                    @Override
207
                    public Component getLazyLoadComponent(String markupId) {
208
                        return new ButtonList(markupId, profiledResource, buttons, memberButtons, adminButtons);
×
209
                    }
210

211
                    @Override
212
                    protected boolean isContentReady() {
213
                        return readyFunction.get();
×
214
                    }
215

216
                    @Override
217
                    public Component getLoadingComponent(String id) {
218
                        if (lazyLoading) {
×
219
                            return new Label(id).setVisible(false);
×
220
                        } else {
221
                            return new Label(id, ResultComponent.getWaitComponentHtml(null)).setEscapeModelStrings(false);
×
222
                        }
223
                    }
224

225
                });
226
            } else {
227
                add(new ButtonList("buttons", profiledResource, buttons, memberButtons, adminButtons));
×
228
            }
229
            finalized = true;
×
230
        }
231
        super.onBeforeRender();
×
232
    }
×
233

234

235
    public abstract class MethodResultComponent<R> extends ResultComponent {
236

237
        private final Supplier<Boolean> readyFunction;
238
        private final Supplier<R> resultFunction;
239

240
        public MethodResultComponent(String id, ReadyFunction readyFunction, ResultFunction<R> resultFunction) {
×
241
            super(id);
×
242
            setOutputMarkupId(true);
×
243
            this.readyFunction = readyFunction;
×
244
            this.resultFunction = resultFunction;
×
245
        }
×
246

247
        /**
248
         * {@inheritDoc}
249
         */
250
        @Override
251
        protected boolean isContentReady() {
252
            return readyFunction.get();
×
253
        }
254

255
        /**
256
         * {@inheritDoc}
257
         */
258
        @Override
259
        public Component getLazyLoadComponent(String markupId) {
260
            R result = resultFunction.get();
×
261
            return getResultComponent(markupId, result);
×
262
        }
263

264
        public abstract Component getResultComponent(String markupId, R result);
265

266
    }
267

268
    public static interface ComponentProvider<T> extends Function<T, Component>, Serializable {
269
    }
270

271
    public static interface ApiResultListProvider<T> extends Function<ApiResponse, List<T>>, Serializable {
272
    }
273

274
    public static interface ReadyFunction extends Supplier<Boolean>, Serializable {
275
    }
276

277
    public static interface ResultFunction<X> extends Supplier<X>, Serializable {
278
    }
279

280
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc