Skip to content

Commit

Permalink
[JENKINS-50315] [JENKINS-22316] Introduce editableType to specify t…
Browse files Browse the repository at this point in the history
…he filtering behavior in editable mode
  • Loading branch information
ikedam committed Apr 29, 2018
1 parent 5e75707 commit c9ffba8
Show file tree
Hide file tree
Showing 9 changed files with 257 additions and 12 deletions.
Expand Up @@ -33,6 +33,8 @@
import java.util.logging.Logger;
import java.util.regex.Pattern;

import javax.annotation.Nonnull;

import jenkins.model.Jenkins;
import hudson.Extension;
import hudson.DescriptorExtensionList;
Expand All @@ -47,7 +49,9 @@
import hudson.util.VariableResolver;

import org.apache.commons.lang.StringUtils;
import org.jvnet.localizer.Localizable;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;

Expand All @@ -64,6 +68,33 @@ public class ExtensibleChoiceParameterDefinition extends SimpleParameterDefiniti

private static final Pattern namePattern = Pattern.compile("[A-Za-z_][A-Za-z_0-9]*");

/**
* How to display choices for input values
*/
public enum EditableType
{
/**
* The input value doesn't work as filter.
*/
NoFilter(Messages._ExtensibleChoiceParameterDefinition_EditableType_NoFilter()),
/**
* The input value works as filter. Only matching values are displayed.
*/
Filter(Messages._ExtensibleChoiceParameterDefinition_EditableType_Filter());

private final Localizable displayName;

private EditableType(Localizable displayName)
{
this.displayName = displayName;
}

public String getDisplayName()
{
return displayName.toString();
}
}

/**
* Deprecated
*/
Expand Down Expand Up @@ -162,12 +193,16 @@ public ExtensibleChoiceParameterDefinition newInstance(StaplerRequest req,
JSONObject formData)
throws hudson.model.Descriptor.FormException
{
return new ExtensibleChoiceParameterDefinition(
ExtensibleChoiceParameterDefinition def = new ExtensibleChoiceParameterDefinition(
formData.getString("name"),
bindJSONWithDescriptor(req, formData, "choiceListProvider", ChoiceListProvider.class),
formData.getBoolean("editable"),
formData.getString("description")
);
if (formData.containsKey("editableType")) {
def.setEditableType(EditableType.valueOf(formData.getString("editableType")));
}
return def;
}

/**
Expand Down Expand Up @@ -313,6 +348,26 @@ public boolean isEditable()
return editable;
}

private EditableType editableType;

/**
* @return How to display choices for input values
*/
@Nonnull
public EditableType getEditableType()
{
return (editableType != null) ? editableType : EditableType.NoFilter;
}

/**
* @param editableType How to display choices for input values
*/
@DataBoundSetter
public void setEditableType(EditableType editableType)
{
this.editableType = editableType;
}

private ChoiceListProvider choiceListProvider = null;

/**
Expand Down
Expand Up @@ -33,7 +33,9 @@ THE SOFTWARE.
<f:textarea previewEndpoint="/markupFormatter/previewDescription" />
</f:entry>
<f:dropdownDescriptorSelector title="${%Choice Provider}" field="choiceListProvider" descriptors="${descriptor.enabledChoiceListProviderList}" />
<f:entry title="${%Editable}" field="editable">
<f:checkbox />
</f:entry>
<f:optionalBlock field="editable" inline="true" title="${%Editable}">
<f:entry title="${%Choices for input value}" field="editableType">
<f:enum>${it.displayName}</f:enum>
</f:entry>
</f:optionalBlock>
</j:jelly>
Expand Up @@ -28,3 +28,5 @@ Description=\u8aac\u660e
Choice\ Provider=\u9078\u629e\u80a2\u306e\u53d6\u5f97\u65b9\u6cd5
# Editable=編集可能にする
Editable=\u7de8\u96c6\u53ef\u80fd\u306b\u3059\u308b
# Choices\ for\ input\ value=入力値に対する選択肢の表示
Choices\ for\ input\ value=\u5165\u529b\u5024\u306b\u5bfe\u3059\u308b\u9078\u629e\u80a2\u306e\u8868\u793a
Expand Up @@ -39,6 +39,7 @@ THE SOFTWARE.
field="value"
items="${it.choiceList}"
editable="${it.editable}"
editableType="${it.editableType.name()}"
/>
</j:scope>
</div>
Expand Down
Expand Up @@ -44,3 +44,5 @@ AddEditedChoiceListProvider.WhenToAdd.Triggered=triggered
AddEditedChoiceListProvider.WhenToAdd.Completed=completed
AddEditedChoiceListProvider.WhenToAdd.CompletedStable=completed stable
AddEditedChoiceListProvider.WhenToAdd.CompletedUnstable=completed stable or unstable
ExtensibleChoiceParameterDefinition.EditableType.NoFilter=Display all choices
ExtensibleChoiceParameterDefinition.EditableType.Filter=Display only matching choices
Expand Up @@ -69,3 +69,7 @@ AddEditedChoiceListProvider.WhenToAdd.Completed=\u30d3\u30eb\u30c9\u5b8c\u4e86\u
AddEditedChoiceListProvider.WhenToAdd.CompletedStable=\u30d3\u30eb\u30c9\u6210\u529f\u6642
# AddEditedChoiceListProvider.WhenToAdd.CompletedUnstable=ビルド成功時(Unstable含む)
AddEditedChoiceListProvider.WhenToAdd.CompletedUnstable=\u30d3\u30eb\u30c9\u6210\u529f\u6642(Unstable\u542b\u3080)
# ExtensibleChoiceParameterDefinition.EditableType.NoFilter=すべての選択肢を表示
ExtensibleChoiceParameterDefinition.EditableType.NoFilter=\u3059\u3079\u3066\u306e\u9078\u629e\u80a2\u3092\u8868\u793a
# ExtensibleChoiceParameterDefinition.EditableType.Filter=一致する選択肢のみ表示
ExtensibleChoiceParameterDefinition.EditableType.Filter=\u4e00\u81f4\u3059\u308b\u9078\u629e\u80a2\u306e\u307f\u8868\u793a
Expand Up @@ -44,6 +44,9 @@ THE SOFTWARE.
<st:attribute name="editable">
specify whether this field is editable.
</st:attribute>
<st:attribute name="editableType">
Candidates for input value when editable. NoFilter, Filter is available.
</st:attribute>
</st:documentation>
<j:scope>
<j:set var="attrs" value="${attrs}" /> <!-- Required for prepareDatabinding -->
Expand All @@ -53,8 +56,8 @@ THE SOFTWARE.
<m:select
xmlns:m="jelly:hudson.util.jelly.MorphTagLibrary"
ATTRIBUTES="${attrs}"
EXCEPT="name field items clazz editable value"
class="setting-input ${attrs.clazz} ${attrs.checkUrl!=null?'validated':''} ${attrs.editable?'staticCombobox':''}"
EXCEPT="name field items clazz editable editableType value"
class="setting-input ${attrs.clazz} ${attrs.checkUrl!=null?'validated':''} ${attrs.editable?'staticCombobox':''} editableType-${attrs.editableType}"
autocomplete="off"
name="${attrs.name ?: '_.'+attrs.field}">
<j:forEach var="value" items="${attrs.items}">
Expand Down
Expand Up @@ -52,13 +52,21 @@ Behaviour.register({"SELECT.staticCombobox": function(e) {
/*
* Changes from the original 3:
* Original behavior: Show candidates that start with the current incomplete input.
* Changed behavior : Show candidates that contain the current incomplete input.
* Changed behavior : Change depends on the class name.
*/
var c = new ComboBox(e,function(value) {
return items.filter(function (item) {
return item.indexOf(value) > -1;
});
}, {});
// Show all the candidates, not concerning with the current incomplete input.
var filter = function(value) {
return items;
};
if ($(e).hasClassName("editableType-Filter")) {
// Show candidates that contain the current incomplete input.
filter = function(value) {
return items.filter(function (item) {
return item.indexOf(value) > -1;
});
};
}
var c = new ComboBox(e,filter,{});

/*
* Changes from the original 4:
Expand Down
Expand Up @@ -46,6 +46,7 @@
import hudson.model.StringParameterValue;
import hudson.model.TaskListener;
import hudson.util.FormValidation;
import jp.ikedam.jenkins.plugins.extensible_choice_parameter.ExtensibleChoiceParameterDefinition.EditableType;
import net.sf.json.JSONObject;

import org.apache.commons.lang.StringUtils;
Expand All @@ -60,6 +61,7 @@
import com.gargoylesoftware.htmlunit.ElementNotFoundException;
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.html.DomElement;
import com.gargoylesoftware.htmlunit.html.HtmlElement;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlOption;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
Expand Down Expand Up @@ -1183,4 +1185,170 @@ public void testSafeDescription() throws Exception {
assertNotNull(page.getElementById("test-expected"));
assertNull(page.getElementById("test-not-expected"));
}


@Test
public void testConfiguration1() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();

ExtensibleChoiceParameterDefinition def = new ExtensibleChoiceParameterDefinition(
"test",
new MockChoiceListProvider(
Arrays.asList("a", "b", "c"),
null
),
false,
"description"
);
p.addProperty(new ParametersDefinitionProperty(def));

j.configRoundtrip(p);

j.assertEqualDataBoundBeans(
def,
p.getProperty(ParametersDefinitionProperty.class).getParameterDefinition("test")
);
}

@Test
public void testConfiguration2() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();

ExtensibleChoiceParameterDefinition def = new ExtensibleChoiceParameterDefinition(
"test",
new MockChoiceListProvider(
Arrays.asList("a", "b", "c"),
"a"
),
true,
"another description"
);
def.setEditableType(EditableType.NoFilter);
p.addProperty(new ParametersDefinitionProperty(def));

j.configRoundtrip(p);

j.assertEqualDataBoundBeans(
def,
p.getProperty(ParametersDefinitionProperty.class).getParameterDefinition("test")
);
}

@Test
public void testConfiguration3() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();

ExtensibleChoiceParameterDefinition def = new ExtensibleChoiceParameterDefinition(
"test",
new MockChoiceListProvider(
Arrays.asList("a", "b", "c"),
"a"
),
true,
"yet another description"
);
def.setEditableType(EditableType.Filter);
p.addProperty(new ParametersDefinitionProperty(def));

j.configRoundtrip(p);

j.assertEqualDataBoundBeans(
def,
p.getProperty(ParametersDefinitionProperty.class).getParameterDefinition("test")
);
}

private List<String> extractCombobox(HtmlElement combobox) throws Exception
{
List<String> ret = new ArrayList<String>();
for (HtmlElement d: combobox.getElementsByTagName("div"))
{
ret.add(d.getTextContent());
}
return ret;
}

@Test
public void testComboboxNoFilter() throws Exception
{
FreeStyleProject p = j.createFreeStyleProject();

ExtensibleChoiceParameterDefinition def = new ExtensibleChoiceParameterDefinition(
"test",
new MockChoiceListProvider(
Arrays.asList("foo/bar/baz", "foo/bar/qux", "bar/baz/qux"),
null
),
true,
"description"
);
def.setEditableType(EditableType.NoFilter);
p.addProperty(new ParametersDefinitionProperty(def));

WebClient wc = j.createAllow405WebClient();
HtmlPage page = wc.getPage(p, "build?delay=0sec");
HtmlTextInput in = page.getElementByName("value");
assertEquals("foo/bar/baz", in.getValueAttribute());

HtmlElement combobox = page.getFirstByXPath("//*[@class='comboBoxList']");

in.setValueAttribute("foo/bar");
in.focus(); // fire onfocus
assertEquals(
Arrays.asList(
"foo/bar/baz",
"foo/bar/qux",
"bar/baz/qux"
),
extractCombobox(combobox)
);
in.blur();
}

@Test
public void testComboboxFilter() throws Exception
{
FreeStyleProject p = j.createFreeStyleProject();

ExtensibleChoiceParameterDefinition def = new ExtensibleChoiceParameterDefinition(
"test",
new MockChoiceListProvider(
Arrays.asList("foo/bar/baz", "foo/bar/qux", "bar/baz/qux"),
null
),
true,
"description"
);
def.setEditableType(EditableType.Filter);
p.addProperty(new ParametersDefinitionProperty(def));

WebClient wc = j.createAllow405WebClient();
HtmlPage page = wc.getPage(p, "build?delay=0sec");
HtmlTextInput in = page.getElementByName("value");
assertEquals("foo/bar/baz", in.getValueAttribute());

HtmlElement combobox = page.getFirstByXPath("//*[@class='comboBoxList']");

in.setValueAttribute("foo/bar");
in.focus(); // fire onfocus
assertEquals(
Arrays.asList(
"foo/bar/baz",
"foo/bar/qux"
),
extractCombobox(combobox)
);
in.blur();

in.setValueAttribute("bar/baz");
in.focus(); // fire onfocus
assertEquals(
Arrays.asList(
"foo/bar/baz",
"bar/baz/qux"
),
extractCombobox(combobox)
);
in.blur();
}
}

0 comments on commit c9ffba8

Please sign in to comment.