Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sage dynamic select input - select2 #709

Merged
merged 16 commits into from
Aug 23, 2021
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/lib/sage_rails/app/sage_components/sage_dynamic_select.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class SageDynamicSelect < SageComponent
set_attribute_schema({
url: String,
label: String,
name: String,
id: String,
has_error: [:optional, TrueClass],
message: [:optional, String],
default_value: [:optional, String, Integer],
default_text: [:optional, String],
paginate: [:optional, TrueClass],
search: [:optional, TrueClass],
clear: [:optional, TrueClass],
placeholder: [:optional, String],
theme: [:optional, String],
required: [:optional, TrueClass],
})
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

<div class="
sage-dynamic-select
<%= "sage-select--error" if component.has_error %>
<%= component.generated_css_classes %>">
<% if component.theme == 'sage' %>
<label class="sage-select__label" for="<%= component.id %>"><%= component.label || component.id %></label>
<% end %>
<select
class="sage-dynamic-select__data"
<%= component.generated_html_attributes.html_safe %>
name="<%= component.name %>"
id="<%= component.id %>"
tabindex="-1"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for the group (no actionable items as of yet): we'll have to be mindful of this and the aria-hidden="true" below for accessibility. Select2 has been actively working on improving their compliance, but it's not quite there, so we'll have to keep a close watch on it.

aria-hidden="true"
data-js-dynamic-select="true"
data-url=<%= component.url %>
data-default-value="<%= component.default_value if component.default_value.present? %>"
teenwolfblitzer marked this conversation as resolved.
Show resolved Hide resolved
data-default-text="<%= component.default_text if component.default_text.present? %>"
data-paginate="<%= component.paginate if component.paginate.present? %>"
data-search="<%= component.search if component.search.present? %>"
data-clear="<%= component.clear if component.clear.present? %>"
data-placeholder="<%= component.placeholder || component.label || component.id %>"
data-theme="<%= component.theme if component.theme.present? %>"
<%= 'required' if component.required.present? %>
>
</select>
<div class="sage-select__message"><%= component.message %></div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
$-dynamic-select-default-height: rem(44);
$-dynamic-select-default-padding: rem(4.4) sage-spacing(sm) 0;
teenwolfblitzer marked this conversation as resolved.
Show resolved Hide resolved
$-dynamic-select-border-color: sage-color(gray, 400);
$-dynamic-select-placeholder-color: sage-color(gray, 400);
$-dynamic-select-color-success: map-get($sage-field-colors, success);
$-dynamic-select-border-box-shadow-size: map-get($sage-field-configs, box-shadow-size);
$-dynamic-select-selected-height: rem(52);
$-dynamic-select-selected-padding: rem(10.8) sage-spacing(sm) 0;
$-dynamic-select-selected-tag-color: sage-color(primary, 400);
$-dynamic-select-selected-tag-background-color: sage-color(primary, 100);
$-dynamic-select-selected-tag-border-radius: sage-border(radius-x-large);
$-dynamic-select-selected-tag-padding: 0 sage-spacing(xs);
$-dynamic-select-selected-tag-height: rem(30);
$-dynamic-select-clear-font-weight: sage-font-weight(regular);
$-dynamic-select-open-arrow: rotate(180deg) scaleX(-1);

/* stylelint-disable selector-max-compound-selectors */

.sage-dynamic-select .sage-dynamic-select__data {
bottom: sage-spacing(xs);
left: 50%;
}

// empty state //
.sage-dynamic-select {
position: relative;
.select2-container--sage .sage-select__arrow::before {
top: rem(3);
}

.select2-container--sage .select2-dropdown--below {
margin-top: sage-spacing(xs);
}

.select2-container--sage .select2-selection--single.sage-select__field {
height: $-dynamic-select-default-height;
padding: $-dynamic-select-default-padding;
}

.select2-container--sage .select2-selection--single.sage-select__field .select2-selection__placeholder {
color: $-dynamic-select-placeholder-color;
}
.select2-container--sage .select2-selection--single.sage-select__field .select2-selection__clear {
float: right;
}
.select2-container--sage .select2-selection--single.sage-select__field .select2-selection__arrow b {
display: none;
}

// open state //
.select2-container--sage.select2-container--open .select2-selection--single.sage-select__field {
border-color: $-dynamic-select-color-success;
box-shadow: $-dynamic-select-border-box-shadow-size $-dynamic-select-color-success;
}

.select2-container--sage.select2-container--open .sage-select__arrow::before {
transform: $-dynamic-select-open-arrow;
}

.select2-container--sage.select2-container--open .sage-select__arrow::before,
.select2-container--sage:hover .sage-select__arrow::before {
color: $-dynamic-select-color-success;
}

// focused state //
.select2-container--sage.select2-container--focus .select2-selection--single.sage-select__field {
border-color: $-dynamic-select-border-color;
}
}

// selected state //
.sage-dynamic-select.sage-select--value-selected .select2-container--sage {
.sage-select__arrow::before {
top: rem(6);
}
.select2-selection--single.sage-select__field {
height: $-dynamic-select-selected-height;
padding: $-dynamic-select-selected-padding;
border-color: $-dynamic-select-border-color;
}
.select2-selection--single.sage-select__field .select2-selection__rendered {
@extend %t-sage-body-small-semi;
display: inline-flex;
flex-direction: row-reverse;
align-items: center;
height: $-dynamic-select-selected-tag-height;
padding: $-dynamic-select-selected-tag-padding;
color: $-dynamic-select-selected-tag-color;
background-color: $-dynamic-select-selected-tag-background-color;
border-radius: $-dynamic-select-selected-tag-border-radius;
}
.select2-selection--single.sage-select__field .select2-selection__rendered .select2-selection__clear {
font-weight: $-dynamic-select-clear-font-weight;
}
}

/* stylelint-enable selector-max-compound-selectors */
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ $-select-color-default: map-get($sage-field-colors, default);
$-select-color-error: map-get($sage-field-colors, error);
$-select-color-label-background: map-get($sage-field-colors, label-background);
$-select-color-success: map-get($sage-field-colors, success);
$-select-filled-top-padding: rem(6);
$-select-filled-top-padding: rem(4.4);
kajabijamell marked this conversation as resolved.
Show resolved Hide resolved
$-select-height: map-get($sage-field-configs, height);
$-select-padding-x: map-get($sage-field-configs, padding);
$-select-padding-label: map-get($sage-field-configs, padding-label);
Expand Down
1 change: 1 addition & 0 deletions packages/sage-assets/lib/stylesheets/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
@import "components/data_card";
@import "components/description";
@import "components/dropdown";
@import "components/dynamic_select";
@import "components/empty_state";
@import "components/expandable_card";
@import "components/feature_toggle";
Expand Down
114 changes: 114 additions & 0 deletions packages/sage-system/lib/dynamicSelect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
Sage.dynamicSelect = (function() {
kajabijamell marked this conversation as resolved.
Show resolved Hide resolved
function init(el) {
this.data = $(el);
this.opts = {
// Sets the url for the dynamic dropdown
url: data.data('url'),
// Enable pagination, the dropdown will infinitely scroll until the result
// set returns 0 results.
paginate: data.data('paginate'),
// Enables typeahead search for the resource, passing a "search" param in
// the requested url
search: data.data('search'),
// Adds a clear button so selection can be cleared/set back to the blank state
clear: data.data('clear'),
// Sets the placeholder for the dynamic dropdown
placeholder: data.data('placeholder'),
// Sets the theme to default to bootstrap/sage
theme: data.data('theme'),
// Sets the width of the dynamic dropdown
width: "100%",
// Chooses which attribute to use for setting the value of the given select
// box, by default this will be the "id", but sometimes is is useful to set
// it to some other attribute
valueAttribute: "id",
};

this.select2 = data.select2({
minimumResultsForSearch: this.opts.search ? 0 : Infinity,
theme: this.opts.theme,
width: this.opts.width,
placeholder: this.opts.placeholder,
allowClear: this.opts.clear,
ajax: {
delay: 500,
url: function () {
return $(data).data("url");
},
cache: true,
data: function (params) {
return {
search: params.term,
page: params.page
};
},
processResults: function (data, params) {
var results = data.results ? data.results : data;
formattedData = $.map(results, function (obj) {
obj.id = obj[this.opts.valueAttribute];
return obj;
}.bind(this));

params.page = params.page || 1;
$(data).select2.data = formattedData;
var showMore = data.totalCount ? (params.page * 25) < data.totalCount : true;
kajabijamell marked this conversation as resolved.
Show resolved Hide resolved

return {
results: formattedData,
pagination: { more: this.opts.paginate && results.length !== 0 && showMore }
};
}.bind(this)
}
});

this.$container = this.data.next();

const { $container, select2 } = this

if (data.data('theme') === 'sage') {
// add sage class
$(select2).data('select2').$selection.addClass("sage-select__field");
// add arrow icon
const iconTemplate = '<i class="sage-select__arrow" aria-hidden="true"></i>';
$container.append(iconTemplate);
}

// set a default option for the input
const defaultValue = data.data('defaultValue');
const defaultText = data.data('defaultText');
const newOption = new Option(defaultText, defaultValue, false, false);
$(select2).append(newOption).trigger('change');

// add event listeners
$(select2).on('select2:open', function (e) {
// callback for when select box is opened
});
$(select2).on('select2:close', function (e) {
// callback for when select box is closed
});

// function for updating input on change //
const updateInput = function() {
const selectedValue = $(this.data).val();
const classActive = 'sage-select--value-selected';
const selectWrapper = $($container).parent();
const selectLabel = $($container).parent().find('.sage-select__label');

Sage.util.isEmptyString(selectedValue) ? selectWrapper.removeClass(classActive) : selectWrapper.addClass(classActive)
Sage.util.isEmptyString(selectedValue) ? selectLabel.hide() : selectLabel.fadeIn(500);
}.bind(this);

// input change event listener
$(this.data).on('change', function(){ updateInput() });

updateInput();
}

function unbind() {
kajabijamell marked this conversation as resolved.
Show resolved Hide resolved
}

return {
init,
unbind,
}
})();
1 change: 1 addition & 0 deletions packages/sage-system/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ require('./hero')
require('./panel-controls')
require('./popover')
require('./search')
require('./dynamicSelect')

require('./init')
1 change: 1 addition & 0 deletions packages/sage-system/lib/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Sage.init = function(elementNamesToInitLegacy) {
initDocumentPresenceListener('[data-js-modaltrigger]', Sage.modal.initTrigger, false);
initDocumentPresenceListener('[data-js-tooltip]', Sage.tooltip.init, Sage.tooltip.unbind);
initDocumentPresenceListener('[data-js-dropdown]', Sage.dropdown.init, Sage.dropdown.unbind);
initDocumentPresenceListener('[data-js-dynamic-select]', Sage.dynamicSelect.init, Sage.dynamicSelect.unbind);
initDocumentPresenceListener('[data-js-sortable]', Sage.sortable.init, Sage.sortable.unbind);
initDocumentPresenceListener('[data-js-tabs]', Sage.tabs.init, Sage.tabs.unbind);
initDocumentPresenceListener('[data-js-copy-button]', Sage.copyButton.init, Sage.copyButton.unbind);
Expand Down