-
Notifications
You must be signed in to change notification settings - Fork 2
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
Changes from all commits
d2e0895
0ca1a81
df17301
f9cc302
92f1734
d86ef39
efb27eb
e3529fd
c4cb6b0
b898bd7
707795e
df2baed
7b14a8a
015ebc2
ed386e1
0fbd075
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<h3>Working with the<code>Sage Dynamic Select Input (Select2)</code></h3> | ||
<p> | ||
The dynamic select input is a sagified version of the select2 input that resides in <code>Kajabi Products.</code> The input fuctionality leverages the Javascript library select2 and includes several options for tailoring the input to the UI. The input can retrieve options from any arbitrary API that will return validly structured JSON.<br/> <br/> Basic usage and complete documentation ca be found | ||
<a href="https://select2.org/getting-started/basic-usage" target="_blank"><code>here</code></a>. | ||
</p> | ||
|
||
<h4>Error Handling</h4> | ||
<p>Much like other sage inputs, the dynamic select includes options to render error messages from the server. By using the <code>has_error</code> attribute, one can trigger the erros states and print an error message that gets placed below the input.</p> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tiny typo here "error states" |
||
|
||
<pre class="prettyprint"> | ||
<code> | ||
// Component API | ||
name: "Input Form Name", | ||
label: "Input Label", | ||
id: "Input id", | ||
required: true | ||
has_error: false | ||
message: "Error message goes here" | ||
url: "Endpoint for API", | ||
default_value: "Prefill select input with default value", | ||
default_text: "Prefill select input with default title", | ||
theme: "Choose Sage or Bootstrap for legacy input", | ||
search: "Allow user to search through retrieved results", | ||
clear: "Allow user to clear input value" | ||
|
||
// Example Implementation | ||
sage_component SageDynamicSelect, { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As @philschanely had mentioned earlier, ideally we'd have a rendered preview of the component in action, but this works as a substitute. As a follow-up, I wonder if we could reference the external JS dependencies (jquery and select2) from a CDN just for this preview page. That'd give us a working preview but prevent the dependencies from being packaged up. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We may be able to add them as peer dependencies in the repo. @voodooGQ would this make sense? |
||
name: "site[home_landing_page_id]", | ||
label: "Landing Page", | ||
id: "site_home_landing_page_id", | ||
url: admin_site_select_options_path(site, :landing_page, { published: true }, format: :json), | ||
default_value: site.home_landing_page.id, | ||
default_text: site.home_landing_page.title, | ||
theme: "sage", | ||
search: true, | ||
clear: false | ||
} | ||
|
||
</code> | ||
</pre> | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
<tr> | ||
<td><%= md('`url`') %></td> | ||
<td><%= md('API URL for retrieving select options') %></td> | ||
<td><%= md('String') %></td> | ||
<td><%= md('`nil`') %></td> | ||
</tr> | ||
<tr> | ||
<td><%= md('`label`') %></td> | ||
<td><%= md('Label for the input') %></td> | ||
<td><%= md('String') %></td> | ||
<td><%= md('`component.id`') %></td> | ||
</tr> | ||
<tr> | ||
<td><%= md('`name`') %></td> | ||
<td><%= md('Input name') %></td> | ||
<td><%= md('String') %></td> | ||
<td><%= md('`nil`') %></td> | ||
</tr> | ||
<tr> | ||
<td><%= md('`id`') %></td> | ||
<td><%= md('Input ID') %></td> | ||
<td><%= md('String') %></td> | ||
<td><%= md('`nil`') %></td> | ||
</tr> | ||
<tr> | ||
<td><%= md('`has_error`') %></td> | ||
<td><%= md('Enabling this property adds the `.sage-input--error` class to the component.') %></td> | ||
<td><%= md('Boolean') %></td> | ||
<td><%= md('`nil`') %></td> | ||
</tr> | ||
<tr> | ||
<td><%= md('`message`') %></td> | ||
<td><%= md('Sets the message text for the component.') %></td> | ||
<td><%= md('String') %></td> | ||
<td><%= md('`nil`') %></td> | ||
</tr> | ||
<tr> | ||
<td><%= md('`placeholder`') %></td> | ||
<td><%= md('Displays placeholder text when the input is empty.') %></td> | ||
<td><%= md('String') %></td> | ||
<td><%= md('`component.label || component.id`') %></td> | ||
</tr> | ||
<tr> | ||
<td><%= md('`default_value`') %></td> | ||
<td><%= md('Default value for the input') %></td> | ||
<td><%= md('String') %></td> | ||
<td><%= md('`nil`') %></td> | ||
</tr> | ||
<tr> | ||
<td><%= md('`default_text`') %></td> | ||
<td><%= md('Default text for the input') %></td> | ||
<td><%= md('String') %></td> | ||
<td><%= md('`nil`') %></td> | ||
</tr> | ||
<tr> | ||
<td><%= md('`paginate`') %></td> | ||
<td><%= md('Allows input to paginate results per 25 items') %></td> | ||
<td><%= md('Boolean') %></td> | ||
<td><%= md('`nil`') %></td> | ||
</tr> | ||
<tr> | ||
<td><%= md('`search`') %></td> | ||
<td><%= md('Displays a search bar for users to search through results') %></td> | ||
<td><%= md('Boolean') %></td> | ||
<td><%= md('`false`') %></td> | ||
</tr> | ||
<tr> | ||
<td><%= md('`clear`') %></td> | ||
<td><%= md('Displays an "x" button for users to clear the current value.') %></td> | ||
<td><%= md('Boolean') %></td> | ||
<td><%= md('`false`') %></td> | ||
</tr> | ||
<tr> | ||
<td><%= md('`required`') %></td> | ||
<td><%= md('Makes input selection required') %></td> | ||
<td><%= md('Boolean') %></td> | ||
<td><%= md('`nil`') %></td> | ||
</tr> | ||
<tr> | ||
<td><%= md('`theme`') %></td> | ||
<td><%= md('Choose from legacy UI or Sage') %></td> | ||
<td><%= md('string') %></td> | ||
<td><%= md('`sage`') %></td> | ||
</tr> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
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], | ||
paginate_size: [:optional, Integer], | ||
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,44 @@ | ||
|
||
<div class=" | ||
sage-dynamic-select | ||
<%= "sage-select--error" if component.has_error %> | ||
<%= component.generated_css_classes %>"> | ||
<% unless component.theme.present? && 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" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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" | ||
data-js-dynamic-select="true" | ||
data-url=<%= component.url %> | ||
data-placeholder="<%= component.placeholder || component.label || component.id %>" | ||
<% if component.default_value.present? %> | ||
data-default-value="<%= component.default_value %>" | ||
<% end %> | ||
<% if component.default_value.present? %> | ||
data-default-text="<%= component.default_text %>" | ||
<% end %> | ||
<% if component.paginate.present? %> | ||
data-paginate="<%= component.paginate %>" | ||
data-paginate-size="<%= component.paginate_size || 25 %>" | ||
<% end %> | ||
<% if component.search.present? %> | ||
data-search="<%= component.search %>" | ||
<% end %> | ||
<% if component.clear.present? %> | ||
data-clear="<%= component.clear %>" | ||
<% end %> | ||
<% if component.theme.present? %> | ||
data-theme="<%= component.theme %>" | ||
<% else %> | ||
data-theme="sage" %> | ||
<% end %> | ||
<%= '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(45); | ||||||
$-dynamic-select-default-padding: rem(4) sage-spacing(sm) 0; | ||||||
$-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(54); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't have an existing spec in Figma for this, so I won't consider it a blocker but do want to note that the height for the component is much taller (3.375rem) than the standard
|
||||||
$-dynamic-select-selected-padding: rem(12) 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 */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😍 Beautifully written!
One suggestion, we can replace the
<br>
tags with a new<p>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note as well that the
md()
helper may be useful to simplify as well.