FEATURE: implements minimum selection for select-kit

This commit is contained in:
Joffrey JAFFEUX 2018-04-05 16:45:19 +02:00 committed by GitHub
parent cd6a99a027
commit f0fe16d824
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 186 additions and 52 deletions

View File

@ -15,7 +15,7 @@
{{tag-chooser
tags=model.parent_tag_name
everyTag=true
limit=1
maximum=1
allowCreate=true
filterPlaceholder="tagging.groups.parent_tag_placeholder"}}
<span class="description">{{i18n 'tagging.groups.parent_tag_description'}}</span>

View File

@ -34,12 +34,12 @@ export default ComboBox.extend(Tags, {
});
});
this.set("limit", parseInt(this.get("limit") || this.get("siteSettings.max_tags_per_topic")));
this.set("maximum", parseInt(this.get("limit") || this.get("maximum") || this.get("siteSettings.max_tags_per_topic")));
},
@computed("hasReachedLimit")
caretIcon(hasReachedLimit) {
return hasReachedLimit ? null : "plus fa-fw";
@computed("hasReachedMaximum")
caretIcon(hasReachedMaximum) {
return hasReachedMaximum ? null : "plus fa-fw";
},
@computed("tags")
@ -135,6 +135,12 @@ export default ComboBox.extend(Tags, {
content.label = joinedTags;
}
if (!this.get("hasReachedMinimum") && isEmpty(this.get("selection"))) {
const key = this.get("minimumLabel") || "select_kit.min_content_not_reached";
const label = I18n.t(key, { count: this.get("minimum") });
content.title = content.name = content.label = label;
}
content.title = content.name = content.value = joinedTags;
return content;

View File

@ -140,10 +140,23 @@ export default SelectKitComponent.extend({
},
computeHeaderContent() {
return {
let content = {
title: this.get("title"),
selection: this.get("selection")
};
if (this.get("noneLabel")) {
if (!this.get("hasSelection")) {
content.title = content.name = content.label = I18n.t(this.get("noneLabel"));
}
} else {
if (!this.get("hasReachedMinimum")) {
const key = this.get("minimumLabel") || "select_kit.min_content_not_reached";
content.title = content.name = content.label = I18n.t(key, { count: this.get("minimum") });
}
}
return content;
},
@computed("filter")
@ -154,7 +167,7 @@ export default SelectKitComponent.extend({
},
validateSelect() {
return this._super() && !this.get("hasReachedLimit");
return this._super() && !this.get("hasReachedMaximum");
},
@computed("computedValues.[]", "computedContent.[]")

View File

@ -25,7 +25,8 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
"isLeftAligned",
"isRightAligned",
"hasSelection",
"hasReachedLimit",
"hasReachedMaximum",
"hasReachedMinimum",
],
isDisabled: false,
isExpanded: false,
@ -71,6 +72,10 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
collectionHeader: null,
allowAutoSelectFirst: true,
highlightedSelection: null,
maximum: null,
minimum: null,
minimumLabel: null,
maximumLabel: null,
init() {
this._super();
@ -188,14 +193,22 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
}
},
validateCreate() { return !this.get("hasReachedLimit"); },
validateCreate() { return !this.get("hasReachedMaximum"); },
validateSelect() { return !this.get("hasReachedLimit"); },
validateSelect() { return !this.get("hasReachedMaximum"); },
@computed("limit", "selection.[]")
hasReachedLimit(limit, selection) {
if (!limit) return false;
return selection.length >= limit;
@computed("maximum", "selection.[]")
hasReachedMaximum(maximum, selection) {
if (!maximum) return false;
selection = makeArray(selection);
return selection.length >= maximum;
},
@computed("minimum", "selection.[]")
hasReachedMinimum(minimum, selection) {
if (!minimum) return true;
selection = makeArray(selection);
return selection.length >= minimum;
},
@computed("shouldFilter", "allowAny", "filter")
@ -212,10 +225,16 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
}
},
@computed("hasReachedLimit", "limit")
maxContentRow(hasReachedLimit, limit) {
if (hasReachedLimit) {
return I18n.t("select_kit.max_content_reached", { count: limit });
@computed("hasReachedMaximum", "hasReachedMinimum", "isExpanded")
validationMessage(hasReachedMaximum, hasReachedMinimum) {
if (hasReachedMaximum && this.get("maximum")) {
const key = this.get("maximumLabel") || "select_kit.max_content_reached";
return I18n.t(key, { count: this.get("maximum") });
}
if (!hasReachedMinimum && this.get("minimum")) {
const key = this.get("minimumLabel") || "select_kit.min_content_not_reached";
return I18n.t(key, { count: this.get("minimum") });
}
},
@ -227,9 +246,9 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
return false;
},
@computed("computedValue", "filter", "collectionComputedContent.[]", "hasReachedLimit", "isLoading")
shouldDisplayCreateRow(computedValue, filter, collectionComputedContent, hasReachedLimit, isLoading) {
if (isLoading || hasReachedLimit) return false;
@computed("computedValue", "filter", "collectionComputedContent.[]", "hasReachedMaximum", "isLoading")
shouldDisplayCreateRow(computedValue, filter, collectionComputedContent, hasReachedMaximum, isLoading) {
if (isLoading || hasReachedMaximum) return false;
if (collectionComputedContent.map(c => c.value).includes(filter)) return false;
if (this.get("allowAny") && filter.length > 0 && this.validateCreate(filter)) return true;
return false;

View File

@ -91,7 +91,7 @@ export default SelectKitComponent.extend({
name: this.get("selection.name") || this.get("noneRowComputedContent.name")
};
if (!this.get("hasSelection") && this.get("noneLabel")) {
if (this.get("noneLabel") && !this.get("hasSelection")) {
content.title = content.name = I18n.t(this.get("noneLabel"));
}
@ -134,7 +134,7 @@ export default SelectKitComponent.extend({
return selection !== this.get("noneRowComputedContent") && !isNone(selection);
},
@computed("computedValue", "filter", "collectionComputedContent.[]", "hasReachedLimit")
@computed("computedValue", "filter", "collectionComputedContent.[]", "hasReachedMaximum", "hasReachedMinimum")
shouldDisplayCreateRow(computedValue, filter) {
return this._super() && computedValue !== filter;
},

View File

@ -37,7 +37,7 @@ export default MultiSelectComponent.extend(Tags, {
});
if (!this.get("unlimitedTagCount")) {
this.set("limit", parseInt(this.get("limit") || this.get("siteSettings.max_tags_per_topic")));
this.set("maximum", parseInt(this.get("limit") || this.get("maximum") || this.get("siteSettings.max_tags_per_topic")));
}
},

View File

@ -33,7 +33,7 @@ export default Ember.Mixin.create({
},
validateCreate(term) {
if (this.get("hasReachedLimit") || !this.site.get("can_create_tag")) {
if (this.get("hasReachedMaximum") || !this.site.get("can_create_tag")) {
return false;
}

View File

@ -41,7 +41,7 @@
computedValue=computedValue
rowComponentOptions=rowComponentOptions
noContentRow=noContentRow
maxContentRow=maxContentRow
validationMessage=validationMessage
}}
{{/unless}}
{{/if}}

View File

@ -30,26 +30,25 @@
}}
{{/if}}
{{#if maxContentRow}}
<li class="select-kit-row max-content">
{{maxContentRow}}
{{#if noContentRow}}
<li class="select-kit-row no-content">
{{noContentRow}}
</li>
{{else}}
{{#if noContentRow}}
<li class="select-kit-row no-content">
{{noContentRow}}
</li>
{{else}}
{{#each collectionComputedContent as |computedContent|}}
{{component rowComponent
computedContent=computedContent
highlighted=highlighted
computedValue=computedValue
templateForRow=templateForRow
onClickRow=onClickRow
onMouseoverRow=onMouseoverRow
options=rowComponentOptions
}}
{{/each}}
{{#if validationMessage}}
<div class="validation-message">
{{validationMessage}}
</div>
{{/if}}
{{#each collectionComputedContent as |computedContent|}}
{{component rowComponent
computedContent=computedContent
highlighted=highlighted
computedValue=computedValue
templateForRow=templateForRow
onClickRow=onClickRow
onMouseoverRow=onMouseoverRow
options=rowComponentOptions
}}
{{/each}}
{{/if}}

View File

@ -40,7 +40,7 @@
computedValue=computedValue
rowComponentOptions=rowComponentOptions
noContentRow=noContentRow
maxContentRow=maxContentRow
validationMessage=validationMessage
}}
{{/if}}
</div>

View File

@ -171,11 +171,6 @@
white-space: nowrap;
}
&.max-content {
white-space: nowrap;
color: $danger;
}
.name {
margin: 0;
overflow: hidden;
@ -211,6 +206,14 @@
padding: 0;
max-height: 200px;
.validation-message {
white-space: nowrap;
color: $danger;
flex: 1 0 auto;
margin: 5px;
padding: 0 2px;
}
.select-kit-collection {
padding: 0;
margin: 0;

View File

@ -1250,7 +1250,8 @@ en:
no_content: No matches found
filter_placeholder: Search...
create: "Create: '{{content}}'"
max_content_reached: "You can only select {{count}} items."
max_content_reached: "You can only select {{count}} item(s)."
min_content_not_reached: "Select at least {{count}} item(s)."
emoji_picker:
filter_placeholder: Search for emoji

View File

@ -160,3 +160,44 @@ componentTest('with limitMatches', {
andThen(() => assert.equal(this.get('subject').el().find(".select-kit-row").length, 2));
}
});
componentTest('with minimum', {
template: '{{multi-select content=content minimum=1}}',
beforeEach() {
this.set('content', ['sam', 'jeff', 'neil']);
},
test(assert) {
this.get('subject').expand();
andThen(() => assert.equal(this.get('subject').validationMessage(), 'Select at least 1 item(s).'));
this.get('subject').selectRowByValue('sam');
andThen(() => {
assert.equal(this.get('subject').header().label(), 'sam');
});
}
});
componentTest('with minimumLabel', {
template: '{{multi-select content=content minimum=1 minimumLabel="test.minimum"}}',
beforeEach() {
I18n.translations[I18n.locale].js.test = { minimum: 'min %{count}' };
this.set('content', ['sam', 'jeff', 'neil']);
},
test(assert) {
this.get('subject').expand();
andThen(() => assert.equal(this.get('subject').validationMessage(), 'min 1'));
this.get('subject').selectRowByValue('jeff');
andThen(() => {
assert.equal(this.get('subject').header().label(), 'jeff');
});
}
});

View File

@ -501,3 +501,44 @@ componentTest('with limitMatches', {
andThen(() => assert.equal(this.get('subject').el().find(".select-kit-row").length, 2));
}
});
componentTest('with minimum', {
template: '{{single-select content=content minimum=1 allowAutoSelectFirst=false}}',
beforeEach() {
this.set('content', ['sam', 'jeff', 'neil']);
},
test(assert) {
this.get('subject').expand();
andThen(() => assert.equal(this.get('subject').validationMessage(), 'Select at least 1 item(s).'));
this.get('subject').selectRowByValue('sam');
andThen(() => {
assert.equal(this.get('subject').header().label(), 'sam');
});
}
});
componentTest('with minimumLabel', {
template: '{{single-select content=content minimum=1 minimumLabel="test.minimum" allowAutoSelectFirst=false}}',
beforeEach() {
I18n.translations[I18n.locale].js.test = { minimum: 'min %{count}' };
this.set('content', ['sam', 'jeff', 'neil']);
},
test(assert) {
this.get('subject').expand();
andThen(() => assert.equal(this.get('subject').validationMessage(), 'min 1'));
this.get('subject').selectRowByValue('jeff');
andThen(() => {
assert.equal(this.get('subject').header().label(), 'jeff');
});
}
});

View File

@ -63,6 +63,7 @@ function selectKit(selector) { // eslint-disable-line no-unused-vars
return {
value: function() { return header.attr('data-value'); },
name: function() { return header.attr('data-name'); },
label: function() { return header.text().trim(); },
icon: function() { return header.find('.icon'); },
title: function() { return header.attr('title'); },
el: function() { return header; }
@ -183,6 +184,16 @@ function selectKit(selector) { // eslint-disable-line no-unused-vars
return rowHelper(find(selector).find('.select-kit-row.none'));
},
validationMessage: function() {
var validationMessage = find(selector).find('.validation-message');
if (validationMessage.length) {
return validationMessage.html().trim();
} else {
return null;
}
},
selectedRow: function() {
return rowHelper(find(selector).find('.select-kit-row.is-selected'));
},