Upgrade Admin to Stimulus 2.0 (#11776)

* Upgrade Stimulus to 2.0

* Convert ArticleController to use Stimulus 2

* Convert ModalController to Stimulus 2

* Convert BufferController to Stimulus 2

* Convert ConfigController to use Stimuus 2

* Convert ImageUploadController to Stimulus 2

* Convert Reaction controller to Stimulus 2
This commit is contained in:
rhymes 2020-12-08 10:43:56 +01:00 committed by GitHub
parent 9bb62598a4
commit 7eb7cef1b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 225 additions and 140 deletions

View file

@ -3,12 +3,15 @@ import ArticleController from '../../controllers/article_controller';
describe('ArticleController', () => {
beforeEach(() => {
document.body.innerHTML = `<div data-controller="article">
<div class="card-body">
document.body.innerHTML = `
<div data-controller="article"
data-article-bg-highlighted-class="bg-highlighted"
data-article-border-highlighted-class="border-highlighted">
<div class="card-body" data-article-target="cardBody">
<button data-action="article#increaseFeaturedNumber"></button>
<button data-action="article#decreaseFeaturedNumber"></button>
<button data-action="article#highlightElement"></button>
<input data-target="article.featuredNumber"></input>
<input data-article-target="featuredNumber"></input>
</div>
</div>`;
@ -23,7 +26,7 @@ describe('ArticleController', () => {
it('increases the featured number input', () => {
const button = document.getElementsByTagName('button')[0];
const input = document.querySelector(
"[data-target='article.featuredNumber']",
"[data-article-target='featuredNumber']",
);
input.value = initialValue;
@ -37,7 +40,7 @@ describe('ArticleController', () => {
it('increases the featured number input', () => {
const button = document.getElementsByTagName('button')[1];
const input = document.querySelector(
"[data-target='article.featuredNumber']",
"[data-article-target='featuredNumber']",
);
input.value = initialValue;

View file

@ -3,8 +3,11 @@ import BufferController from '../../controllers/buffer_controller';
describe('BufferController', () => {
beforeEach(() => {
document.body.innerHTML = `<div data-controller="buffer">
<h2 data-target="buffer.header"></h2>
document.body.innerHTML = `
<div data-controller="buffer"
data-buffer-bg-highlighted-class="bg-highlighted"
data-buffer-border-highlighted-class="border-highlighted">
<h2 data-buffer-target="header"></h2>
<button data-action="buffer#tagBufferUpdateConfirmed"></button>
<button data-action="buffer#tagBufferUpdateDismissed"></button>
<button data-action="buffer#highlightElement"></button>

View file

@ -3,11 +3,12 @@ import ConfigController from '../../controllers/config_controller';
describe('ConfigController', () => {
beforeEach(() => {
document.body.innerHTML = `<div data-controller="config">
document.body.innerHTML = `
<div data-controller="config">
<button data-action="click->config#activateEmailAuthModal">
Disable
</button>
<div data-target="config.configModalAnchor"></div>
<div data-config-target="configModalAnchor"></div>
</div>`;
global.scrollTo = jest.fn();
@ -20,7 +21,7 @@ describe('ConfigController', () => {
it('builds and adds a Modal to the page', () => {
const button = document.getElementsByTagName('button')[0];
const modalAnchor = document.querySelector(
'[data-target="config.configModalAnchor"]',
'[data-config-target="configModalAnchor"]',
);
button.click();

View file

@ -1,7 +1,8 @@
import { Controller } from 'stimulus';
export default class ArticleController extends Controller {
static targets = ['featuredNumber'];
static classes = ['bgHighlighted', 'borderHighlighted'];
static targets = ['featuredNumber', 'cardBody'];
increaseFeaturedNumber() {
// Increases the article's chances of being seen
@ -16,18 +17,12 @@ export default class ArticleController extends Controller {
}
highlightElement() {
const card = this.element.getElementsByClassName('card-body')[0];
card.classList.add('bg-highlighted', 'border-highlighted');
const card = this.cardBodyTarget;
card.classList.add(this.bgHighlightedClass, this.borderHighlightedClass);
setTimeout(() => {
card.classList.remove('bg-highlighted');
card.classList.remove(this.bgHighlightedClass);
}, 350);
}
get articleId() {
return parseInt(this.data.get('id'), 10);
}
set articleId(value) {
this.data.set('id', value);
}
}

View file

@ -1,26 +1,33 @@
import { Controller } from 'stimulus';
const CONFIRM_BADGE_HTML =
'<span class="ml-2 badge badge-success">Confirm</span>';
const DISMISS_BADGE_HTML =
'<span class="ml-2 badge badge-danger">Dismiss</span>';
export default class BufferController extends Controller {
static classes = ['bgHighlighted', 'borderHighlighted'];
static targets = ['header', 'bodyText'];
tagBufferUpdateConfirmed() {
this.clearPreviousBadge();
this.headerTarget.innerHTML +=
'<span class="ml-2 badge badge-success">Confirm</span>';
this.headerTarget.innerHTML += CONFIRM_BADGE_HTML;
}
tagBufferUpdateDismissed() {
this.clearPreviousBadge();
this.headerTarget.innerHTML +=
'<span class="ml-2 badge badge-danger">Dismiss</span>';
this.headerTarget.innerHTML += DISMISS_BADGE_HTML;
}
highlightElement() {
this.element.classList.add('bg-highlighted', 'border-highlighted');
this.element.classList.add(
this.bgHighlightedClass,
this.borderHighlightedClass,
);
setTimeout(() => {
this.element.classList.remove('bg-highlighted');
this.element.classList.remove(this.bgHighlightedClass);
}, 350);
}
@ -36,12 +43,4 @@ export default class BufferController extends Controller {
badge.remove();
}
}
get bufferUpdateId() {
return parseInt(this.data.get('id'), 10);
}
set bufferUpdateId(value) {
this.data.set('id', value);
}
}

View file

@ -11,8 +11,10 @@ const emailAuthSettingsSection = document.getElementById(
const emailAuthModalTitle = 'Disable Email address registration';
// TODO: Remove the sentence "You must update site config to save this action!"
// once we build more robust flow for Admin/Config
const emailAuthModalBody =
'<p>If you disable Email address as a registration option, people cannot create an account with their email address.</p><p>However, people who have already created an account using their email address can continue to login.</p><p><strong>You must confirm and update site config to save below this action.</strong></p>';
const emailAuthModalBody = `
<p>If you disable Email address as a registration option, people cannot create an account with their email address.</p>
<p>However, people who have already created an account using their email address can continue to login.</p>
<p><strong>You must confirm and update site config to save below this action.</strong></p>`;
export default class ConfigController extends Controller {
static targets = [

View file

@ -2,20 +2,22 @@ import { Controller } from 'stimulus';
export default class ImageUploadController extends Controller {
static targets = ['fileField', 'imageResult'];
static values = { url: String };
onFormSubmit(event) {
event.preventDefault();
let token = document.getElementsByName('authenticity_token')[0].value;
let image = this.fileFieldTarget.files[0];
const token = document.getElementsByName('authenticity_token')[0].value;
const image = this.fileFieldTarget.files[0];
let formData = new FormData();
formData.append('authenticity_token', token);
formData.append('image', image);
fetch('/image_uploads', {
fetch(this.urlValue, {
method: 'POST',
headers: {
'X-CSRF_Token': window.csrfToken,
'X-CSRF-Token': window.csrfToken,
},
body: formData,
credentials: 'same-origin',

View file

@ -1,11 +1,12 @@
import { Controller } from 'stimulus';
export default class ModalController extends Controller {
static classes = ['hidden'];
static targets = ['toggle'];
toggleModal() {
if (this.toggleTarget) {
this.toggleTarget.classList.toggle('hidden');
if (this.hasToggleTarget) {
this.toggleTarget.classList.toggle(this.hiddenClass);
}
}
}

View file

@ -1,13 +1,16 @@
/* eslint-disable no-alert */
import { Controller } from 'stimulus';
export default class ReactionController extends Controller {
static targets = ['invalid', 'confirmed'];
static values = {
id: Number,
url: String,
};
// eslint-disable-next-line class-methods-use-this
/* eslint no-alert: "error" */
updateReaction(id, status) {
fetch(`/admin/reactions/${id}`, {
updateReaction(status) {
const id = this.idValue;
fetch(this.urlValue, {
method: 'PATCH',
headers: {
Accept: 'application/json',
@ -28,46 +31,45 @@ export default class ReactionController extends Controller {
this.element.remove();
document.getElementById(`js__reaction__div__hr__${id}`).remove();
} else {
alert(json.error);
window.alert(json.error);
}
})
.catch((error) => {
alert(error);
window.alert(error);
}),
);
}
updateReactionInvalid() {
this.updateReaction(this.reactionId, this.invalidStatus);
this.updateReaction(this.invalidStatus);
}
updateReactionConfirmed() {
this.updateReaction(this.reactionId, this.confirmedStatus);
this.updateReaction(this.confirmedStatus);
}
reactableUserCheck() {
if (this.reactableType === 'user') {
// eslint-disable-next-line no-restricted-globals
if (confirm('You are confirming a User vomit reaction; are you sure?')) {
this.updateReaction(this.reactionId, this.confirmedStatus);
if (
window.confirm(
'You are confirming a User vomit reaction; are you sure?',
)
) {
this.updateReaction(this.confirmedStatus);
}
} else {
this.updateReaction(this.reactionId, this.confirmedStatus);
this.updateReaction(this.confirmedStatus);
}
}
get reactionId() {
return parseInt(this.data.get('id'), 10);
}
get confirmedStatus() {
return this.confirmedTarget.dataset.status;
}
get reactableType() {
return this.confirmedTarget.dataset.reactable;
}
get confirmedStatus() {
return this.confirmedTarget.dataset.status;
}
get invalidStatus() {
return this.invalidTarget.dataset.altstatus;
}

View file

@ -4,19 +4,29 @@
}
</style>
<div class="card my-3" data-controller="article" data-article-id="<%= article.id %>">
<div class="card my-3"
data-controller="article"
data-article-bg-highlighted-class="bg-highlighted"
data-article-border-highlighted-class="border-highlighted">
<div class="card-header">
<h2>
<a href="<%= article.path %>" target="_blank" rel="noopener">
<%= article.title %>
</a>
</h2>
<div>
<a href="<%= article.path %>/edit" target="_blank" rel="noopener"><span class="btn btn-sm btn-primary">Edit</span></a>
<button class="btn btn-sm btn-success" data-action="article#increaseFeaturedNumber">Boost</button>
<a class="btn btn-sm btn-secondary" href="/admin/users/<%= article.user_id %>" target="_blank" rel="noopener">Manage User</a>
<a href="/admin/users/<%= article.user_id %>" class="badge badge-light">@<%= article.user&.username %></a>
<a class="btn btn-sm btn-secondary" href="<%= admin_user_path(article.user_id) %>" target="_blank" rel="noopener">Manage User</a>
<a href="<%= admin_user_path(article.user_id) %>" class="badge badge-light">@<%= article.user&.username %></a>
<span class="badge badge-light">❤️ <%= article.public_reactions_count %> 💬 <%= article.comments_count %></span>
<span class="badge badge-light">
<% if article.published_from_feed? && !article.published? %>
RSS Import <%= article.created_at.strftime("%b %d, %Y") %>
@ -28,10 +38,10 @@
<%= article.published_at&.strftime("%b %d, %Y") %>
<% end %>
</span>
<% article.decorate.cached_tag_list_array.each do |tag| %>
<a class="badge badge-secondary" href='/t/<%= tag %>'>#<%= tag %></a>
<% end %>
<% article.decorate.cached_tag_list_array.each do |tag| %>
<a class="badge badge-secondary" href='<%= tag_path(tag) %>'>#<%= tag %></a>
<% end %>
</div>
</div>
@ -40,6 +50,7 @@
<% background_color = approved %>
<div
data-article-target="cardBody"
class="card-body <%= background_color %> <%= "bg-danger" if !article.published? && article.published_from_feed? && !article.user&.feed_admin_publish_permission? %>">
<% if article.video %>
@ -60,7 +71,7 @@
<%= note.author_id ? User.find(note.author_id).username : "No Author" %></em> - <%= note.content %>
</p>
<% end %>
<p><a href="/admin/users/<%= article.user_id %>">View All</a></p>
<p><a href="<%= admin_user_path(article.user_id) %>">View All</a></p>
<% end %>
<% end %>
<% if article.main_image.present? %>
@ -77,8 +88,11 @@
<div class="row">
<div class="form-group col">
<label for="featured_number_<%= article.id %>">Featured Number:</label>
<input id="featured_number_<%= article.id %>" class="form-control" name="article[featured_number]"
value="<%= article.featured_number %>" data-target="article.featuredNumber">
<input id="featured_number_<%= article.id %>"
class="form-control"
name="article[featured_number]"
value="<%= article.featured_number %>"
data-article-target="featuredNumber">
</div>
<div class="form-group col">
<label for="author_id_<%= article.id %>">Author ID:</label>

View file

@ -18,28 +18,40 @@
<div class="col-12">
<ul class="nav nav-tabs nav-fill">
<li class="nav-item">
<a href="/admin/articles" class="nav-link <%= "active" if params[:state].blank? %>">Hot</a>
<a href="<%= admin_articles_path %>" class="nav-link <%= "active" if params[:state].blank? %>">Hot</a>
</li>
<li class="nav-item">
<a href="/admin/articles?state=chronological" class="nav-link <%= "active" if params[:state] == "chronological" %>">Chronological</a>
<a
href="<%= admin_articles_path(state: :chronological) %>"
class="nav-link <%= "active" if params[:state] == "chronological" %>">Chronological</a>
</li>
<li class="nav-item">
<a href="/admin/articles?state=satellite" class="nav-link <%= "active" if params[:state] == "satellite" %>">Satellite</a>
<a
href="<%= admin_articles_path(state: :satellite) %>"
class="nav-link <%= "active" if params[:state] == "satellite" %>">Satellite</a>
</li>
<li class="nav-item">
<span class="nav-link disabled">Not Buffered:</span>
</li>
<li class="nav-item">
<a href="/admin/articles?state=not-buffered-0.25" class="nav-link <%= "active" if params[:state] == "not-buffered-0.25" %>">6hr</a>
<a
href="<%= admin_articles_path(state: "not-buffered-0.25") %>"
class="nav-link <%= "active" if params[:state] == "not-buffered-0.25" %>">6hr</a>
</li>
<li class="nav-item">
<a href="/admin/articles?state=not-buffered-1" class="nav-link <%= "active" if params[:state] == "not-buffered-1" %>">1d</a>
<a
href="<%= admin_articles_path(state: "not-buffered-1") %>"
class="nav-link <%= "active" if params[:state] == "not-buffered-1" %>">1d</a>
</li>
<li class="nav-item">
<a href="/admin/articles?state=not-buffered-7" class="nav-link <%= "active" if params[:state] == "not-buffered-7" %>">7d</a>
<a
href="<%= admin_articles_path(state: "not-buffered-7") %>"
class="nav-link <%= "active" if params[:state] == "not-buffered-7" %>">7d</a>
</li>
<li class="nav-item">
<a href="/admin/articles?state=satellite-not-buffered" class="nav-link <%= "active" if params[:state] == "satellite-not-buffered" %>">Satellite</a>
<a
href="href="<%= admin_articles_path(state: "satellite-not-buffered") %>""
class="nav-link <%= "active" if params[:state] == "satellite-not-buffered" %>">Satellite</a>
</li>
</ul>
<% if params[:state] && params[:state].include?("top-") && params[:state] != "top-3" && params[:state] != "top-6" %>
@ -54,31 +66,53 @@
<summary style="font-size: 1.3em; cursor: pointer;">Suggested Tweets (<%= @pending_buffer_updates.size %>)</summary>
<% @pending_buffer_updates.each do |buffer_update| %>
<% next unless buffer_update.article %>
<div class="card my-3" id="suggested-tweet-<%= buffer_update.id %>" data-controller="buffer" data-action="load@window->buffer#autosizeBodyText">
<div
class="card my-3"
id="suggested-tweet-<%= buffer_update.id %>"
data-controller="buffer"
data-action="load@window->buffer#autosizeBodyText"
data-buffer-bg-highlighted-class="bg-highlighted"
data-buffer-border-highlighted-class="border-highlighted">
<div class="card-header">
<h2 class="my-0" data-target="buffer.header"><%= buffer_update.article.title %></h2>
<h2 class="my-0" data-buffer-target="header"><%= buffer_update.article.title %></h2>
</div>
<div class="card-body">
<h4>Score: <%= buffer_update.article.score %></h4>
<div class="blockquote">
<%= HTML_Truncator.truncate(buffer_update.article.processed_html, 50, ellipsis: "<a class=\"comment-read-more\" href=\"#{buffer_update.article.path}\">... Read Entire Post</a>").html_safe %>
<%= HTML_Truncator.truncate(
buffer_update.article.processed_html,
50,
ellipsis: "<a class=\"comment-read-more\" href=\"#{buffer_update.article.path}\">... Read Entire Post</a>",
).html_safe %>
</div>
<hr />
<code><b><%= Tag.find_by(id: buffer_update.tag_id)&.name || buffer_update.social_service_name %>:</b></code>
<%= form_with url: admin_buffer_update_path(buffer_update.id), class: "buffer-form buffer-confirm", html: { data: { action: "submit->buffer#highlightElement" } } do |f| %>
<%= form_with(
url: admin_buffer_update_path(buffer_update.id),
class: "buffer-form buffer-confirm",
html: { data: { action: "submit->buffer#highlightElement" } },
) do |f| %>
<div class="form-group">
<input name="utf8" type="hidden" value="✓">
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>" />
<input type="hidden" name="_method" value="patch" />
<input type="hidden" name="status" value="confirmed" />
<textarea class="w-100 suggested-tweet-body-text" name="body_text" class="form-control" data-target="buffer.bodyText"><%= buffer_update.body_text %></textarea>
<textarea
class="w-100 suggested-tweet-body-text"
name="body_text"
class="form-control"
data-buffer-target="bodyText"><%= buffer_update.body_text %></textarea>
</div>
<button value="confirmed" name="status" class="btn btn-success" data-action="buffer#tagBufferUpdateConfirmed">Confirm</button>
<% end %>
<%= form_with url: admin_buffer_update_path(buffer_update.id), class: "buffer-form buffer-dismiss", html: { data: { action: "submit->buffer#highlightElement" } } do |f| %>
<%= form_with(
url: admin_buffer_update_path(buffer_update.id),
class: "buffer-form buffer-dismiss",
html: { data: { action: "submit->buffer#highlightElement" } },
) do |f| %>
<div class="form-group">
<input name="utf8" type="hidden" value="✓">
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>" />

View file

@ -135,7 +135,9 @@
<h3 class="crayons-subtitle-3 mb-3">
General settings
</h3>
<div class="crayons-field crayons-field--checkbox <%= invite_only_mode_or_no_enabled_auth_options ? "crayons-tooltip" : "" %>" data-tooltip="Unchecking this will enable Email Registration">
<div
class="crayons-field crayons-field--checkbox <%= invite_only_mode_or_no_enabled_auth_options ? "crayons-tooltip" : "" %>"
data-tooltip="Unchecking this will enable Email Registration">
<%= f.check_box :invite_only_mode,
checked: invite_only_mode_or_no_enabled_auth_options,
data: { action: "config#adjustAuthenticationOptions", target: "config.inviteOnlyMode" },
@ -171,7 +173,7 @@
</p>
<div
class="enabled-indicator <%= SiteConfig.allow_email_password_registration ? "visible" : "" %>"
data-target="config.enabledIndicator">
data-config-target="enabledIndicator">
<%= inline_svg_tag("checkmark.svg", aria: true, class: "crayons-icon admin-config-checkmark", title: "Checkmark") %>
<small class="crayons-field__description ml-1">Enabled</small>
</div>
@ -181,7 +183,7 @@
class="crayons-btn crayons-btn--secondary"
id="email-auth-enable-edit-btn"
data-button-text="<%= SiteConfig.allow_email_password_registration ? "edit" : "enable" %>"
data-target="config.emailAuthSettingsBtn"
data-config-target="emailAuthSettingsBtn"
data-action="click->config#enableOrEditEmailAuthSettings"
<%= disabled_attr_on_auth_provider_enablebtn %>>
<%= SiteConfig.allow_email_password_registration ? "Edit" : "Enable" %>
@ -217,7 +219,10 @@
<div class="crayons-field--checkbox">
<%= f.check_box :require_captcha_for_email_password_registration,
checked: SiteConfig.require_captcha_for_email_password_registration,
data: { action: "config#toggleGoogleRecaptchaFields", target: "config.requireCaptchaForEmailPasswordRegistration" },
data: {
action: "config#toggleGoogleRecaptchaFields",
"config-target": "requireCaptchaForEmailPasswordRegistration"
},
class: "crayons-checkbox mt-2" %>
<div>
<%= admin_config_label :require_captcha_for_email_password_registration, "Enable Google reCAPTCHA for email password registration" %>

View file

@ -23,16 +23,21 @@
<div class="card-body" style="overflow: scroll; max-height: 500px;">
<% @vomits.each do |reaction| %>
<% next if (reaction.reactable_type == "Article" && !reaction.reactable.published) || (reaction.reactable_type == "User" && reaction.reactable&.banished?) %>
<div class="d-flex justify-content-between" data-controller="reaction" data-reaction-id="<%= reaction.id %>">
<div
class="d-flex justify-content-between"
data-controller="reaction"
data-reaction-id-value="<%= reaction.id %>"
data-reaction-url-value="<%= admin_reaction_path(reaction.id) %>">
<span>
🤢 <a href="<%= reaction.user.path %>">@<%= reaction.user.username %></a>
🤢 <a href="<%= reaction.user.path %>" target="_blank" rel="noopener">@<%= reaction.user.username %></a>
<% if reaction.user_id == SiteConfig.mascot_user_id %>
<strong>(auto-generated)</strong>
<% end %>
</span>
<span>
<strong><%= reaction.reactable_type %>:</strong>
<a href="<%= reaction.reactable.path %>"><%= reaction.reactable_type == "User" ? reaction.reactable.username : reaction.reactable.title %></a>
<a href="<%= reaction.reactable.path %>" target="_blank" rel="noopener"><%= reaction.reactable_type == "User" ? reaction.reactable.username : reaction.reactable.title %></a>
<% if reaction.reactable_type == "User" && reaction.reactable.banned %>
<span class="badge badge-danger">Suspended</span>
<% end %>
@ -48,7 +53,7 @@
type="button"
data-reactable="user"
data-status="confirmed"
data-target="reaction.confirmed"
data-reaction-target="confirmed"
data-action="reaction#reactableUserCheck">
CONFIRM
</button>
@ -58,7 +63,7 @@
type="button"
data-reactable="non-user"
data-status="confirmed"
data-target="reaction.confirmed"
data-reaction-target="confirmed"
data-action="reaction#reactableUserCheck">
CONFIRM
</button>
@ -67,7 +72,7 @@
class="btn btn-danger btn-sm"
type="button"
data-altstatus="invalid"
data-target="reaction.invalid"
data-reaction-target="invalid"
data-action="reaction#updateReactionInvalid">
INVALID
</button>
@ -77,7 +82,7 @@
class="btn btn-warning text-white btn-sm"
type="button"
data-altstatus="invalid"
data-target="reaction.invalid"
data-reaction-target="invalid"
data-action="reaction#updateReactionInvalid">
INVALIDATE
</button>
@ -86,7 +91,7 @@
class="btn btn-success btn-sm"
type="button"
data-status="confirmed"
data-target="reaction.confirmed"
data-reaction-target="confirmed"
data-action="reaction#updateReactionConfirmed">
MARK AS VALID
</button>

View file

@ -1,9 +1,11 @@
<header class="flex items-center mb-6">
<h2 class="crayons-title mb-4">Listings</h2>
<a href="/admin/listings/categories"
aria-label="Listing Categories"
class="ml-auto crayons-btn">
<a
href="<%= admin_listing_categories_path %>"
aria-label="Listing Categories"
class="ml-auto crayons-btn">
Listing Categories
</a>
</header>
@ -57,8 +59,19 @@
<td><%= listing.bumped_at ? "#{time_ago_in_words(listing.bumped_at)} ago" : "Draft" %></td>
</tr>
<tr>
<td class="buffer-cell border-0" colspan="7" data-controller="buffer" data-action="load@window->buffer#autosizeBodyText">
<button class="btn btn-secondary" data-toggle="collapse" data-target="#buffering-area-for-listing-<%= listing.id %>" aria-expanded="false" aria-controls="buffering-area-for-listing-<%= listing.id %>">
<td
class="buffer-cell border-0"
colspan="7"
data-controller="buffer"
data-action="load@window->buffer#autosizeBodyText"
data-buffer-bg-highlighted-class="bg-highlighted"
data-buffer-border-highlighted-class="border-highlighted">
<button
class="btn btn-secondary"
data-toggle="collapse"
data-target="#buffering-area-for-listing-<%= listing.id %>"
aria-expanded="false"
aria-controls="buffering-area-for-listing-<%= listing.id %>">
Share to Buffer
</button>
@ -73,7 +86,7 @@
<%= form_with url: admin_buffer_updates_path, html: { data: { action: "submit->buffer#highlightElement" } } do %>
<input type="hidden" name="social_channel" value="listings_twitter" />
<input type="hidden" name="listing_id" value="<%= listing.id %>" />
<textarea class="form-control" wrap="hard" name="tweet" maxlength="255" data-target="buffer.bodyText">
<textarea class="form-control" wrap="hard" name="tweet" maxlength="255" data-buffer-target="bodyText">
📋 New <%= community_name %> Listing!&#013&#013Category: <%= listing.category %>&#013&#013<%= listing.title %>&#013&#013
<% if listing.user.twitter_username? %>
Posted by @<%= listing.user.twitter_username %>

View file

@ -1,9 +1,9 @@
<span data-controller="modal">
<span data-controller="modal" data-modal-hidden-class="hidden">
<button class="crayons-btn" type="button" data-action="modal#toggleModal">
Add group
</button>
<div id="add-group-modal" class="hidden" data-target="modal.toggle">
<div id="add-group-modal" class="hidden" data-modal-target="toggle">
<div class="crayons-modal crayons-modal--s absolute">
<div class="crayons-modal__box">
<header class="crayons-modal__box__header">

View file

@ -1,9 +1,9 @@
<span data-controller="modal" class="mx-1">
<span data-controller="modal" class="mx-1" data-modal-hidden-class="hidden">
<button data-action="modal#toggleModal" class="crayons-btn">
Add Field
</button>
<div id="add-<%= group_name %>-profile-field-modal" class="hidden" data-target="modal.toggle">
<div id="add-<%= group_name %>-profile-field-modal" class="hidden" data-modal-target="toggle">
<div class="crayons-modal crayons-modal--s">
<div class="crayons-modal__box">
<header class="crayons-modal__box__header">

View file

@ -1,9 +1,9 @@
<span data-controller="modal" class="mx-1">
<span data-controller="modal" class="mx-1" data-modal-hidden-class="hidden">
<button data-action="modal#toggleModal" class="crayons-btn crayons-btn--secondary">
Edit group
</button>
<div id="edit-group-<%= group_name %>-modal" class="hidden" data-target="modal.toggle">
<div id="edit-group-<%= group_name %>-modal" class="hidden" data-modal-target="toggle">
<div class="crayons-modal crayons-modal--s">
<div class="crayons-modal__box">
<header class="crayons-modal__box__header">

View file

@ -1,15 +1,21 @@
<div class="crayons-card p-6" data-controller="image-upload">
<div class="crayons-card p-6" data-controller="image-upload" data-image-upload-url-value="<%= image_uploads_path %>">
<h2 class="crayons-title mb-4">Upload an Image</h2>
<p class="mb-6">Quickly upload an image.</p>
<% # Multipart, remote forms are tricky, the data is actually sent in the Stimulus controller. %>
# Multipart, remote forms are tricky, the data is actually sent in the Stimulus controller. %>
<%= form_with(url: image_uploads_path, multipart: true) do |f| %>
<div class="form-group">
<%= f.label(:image, "Image:") %>
<%= f.file_field "image", class: "form-control", required: true, data: { target: "image-upload.fileField" } %>
<%= f.file_field(
"image",
class: "form-control",
required: true,
accept: "image/*",
data: { "image-upload-target": "fileField" },
) %>
</div>
<%= f.submit "Upload", class: "btn btn-primary", data: { action: "image-upload#onFormSubmit" } %>
<% end %>
<div class="mt-4 d-none" data-target="image-upload.imageResult">
<div class="mt-4 d-none" data-image-upload-target="imageResult">
</div>
</div>

View file

@ -164,7 +164,7 @@
"prop-types": "^15.7.2",
"pusher-js": "^7.0.2",
"rails-erb-loader": "^5.5.2",
"stimulus": "^1.1.1",
"stimulus": "^2.0.0",
"twilio-video": "^2.9.0",
"web-share-wrapper": "^0.2.1"
},

View file

@ -1608,29 +1608,29 @@
dependencies:
"@sinonjs/commons" "^1.7.0"
"@stimulus/core@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@stimulus/core/-/core-1.1.1.tgz#42b0cfe5b73ca492f41de64b77a03980bae92c82"
integrity sha512-PVJv7IpuQx0MVPCBblXc6O2zbCmU8dlxXNH4bC9KK6LsvGaE+PCXXrXQfXUwAsse1/CmRu/iQG7Ov58himjiGg==
"@stimulus/core@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@stimulus/core/-/core-2.0.0.tgz#140c85318d6a8a8210c0faf182223b8459348877"
integrity sha512-ff70GafKtzc8zQ1/cG+UvL06GcifPWovf2wBEdjLMh9xO2GOYURO3y2RYgzIGYUIBefQwyfX2CLfJdZFJrEPTw==
dependencies:
"@stimulus/mutation-observers" "^1.1.1"
"@stimulus/mutation-observers" "^2.0.0"
"@stimulus/multimap@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@stimulus/multimap/-/multimap-1.1.1.tgz#b95e3fd607345ab36e5d5b55486ee1a12d56b331"
integrity sha512-26R1fI3a8uUj0WlMmta4qcfIQGlagegdP4PTz6lz852q/dXlG6r+uPS/bx+H8GtfyS+OOXVr3SkZ0Zg0iRqRfQ==
"@stimulus/multimap@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@stimulus/multimap/-/multimap-2.0.0.tgz#420cfa096ed6538df4a91dbd2b2842c1779952b2"
integrity sha512-pMBCewkZCFVB3e5mEMoyO9+9aKzHDITmf3OnPun51YWxlcPdHcwbjqm1ylK63fsoduIE+RowBpFwFqd3poEz4w==
"@stimulus/mutation-observers@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@stimulus/mutation-observers/-/mutation-observers-1.1.1.tgz#0f6c6f081308427fed2a26360dda0c173b79cfc0"
integrity sha512-/zCnnw1KJlWO2mrx0yxYaRFZWMGnDMdOgSnI4hxDLxdWVuL2HMROU8FpHWVBLjKY3T9A+lGkcrmPGDHF3pfS9w==
"@stimulus/mutation-observers@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@stimulus/mutation-observers/-/mutation-observers-2.0.0.tgz#3dbe37453bda47a6c795a90204ee8d77a799fb87"
integrity sha512-kx4VAJdPhIGBQKGIoUDC2tupEKorG3A+ckc2b1UiwInKTMAC1axOHU8ebcwhaJIxRqIrs8//4SJo9YAAOx6FEg==
dependencies:
"@stimulus/multimap" "^1.1.1"
"@stimulus/multimap" "^2.0.0"
"@stimulus/webpack-helpers@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@stimulus/webpack-helpers/-/webpack-helpers-1.1.1.tgz#eff60cd4e58b921d1a2764dc5215f5141510f2c2"
integrity sha512-XOkqSw53N9072FLHvpLM25PIwy+ndkSSbnTtjKuyzsv8K5yfkFB2rv68jU1pzqYa9FZLcvZWP4yazC0V38dx9A==
"@stimulus/webpack-helpers@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@stimulus/webpack-helpers/-/webpack-helpers-2.0.0.tgz#54296d2a2dffd4f962d2e802d99a3fdd84b8845f"
integrity sha512-D6tJWsAC024MwGEIKlUVYU8Ln87mlrmiwHvYAjipg+s8H4eLxUMQ3PZkWyPevfipH+oR3leuHsjYsK1gN5ViQA==
"@stoplight/better-ajv-errors@0.0.3":
version "0.0.3"
@ -17206,13 +17206,13 @@ stickyfill@^1.1.1:
resolved "https://registry.yarnpkg.com/stickyfill/-/stickyfill-1.1.1.tgz#39413fee9d025c74a7e59ceecb23784cc0f17f02"
integrity sha1-OUE/7p0CXHSn5ZzuyyN4TMDxfwI=
stimulus@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/stimulus/-/stimulus-1.1.1.tgz#53c2fded6849e7b85eed3ed8dd76e33abd74bec5"
integrity sha512-R0mBqKp48YnRDZOxZ8hiOH4Ilph3Yj78CIFTBkCwyHs4iGCpe7xlEdQ7cjIxb+7qVCSxFKgxO+mAQbsNgt/5XQ==
stimulus@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/stimulus/-/stimulus-2.0.0.tgz#713c8b91a72ef90914b90955f0e705f004403047"
integrity sha512-xipy7BS5TVpg4fX6S8LhrYZp7cmHGjmk09WSAiVx1gF5S5g43IWsuetfUhIk8HfHUG+4MQ9nY0FQz4dRFLs/8w==
dependencies:
"@stimulus/core" "^1.1.1"
"@stimulus/webpack-helpers" "^1.1.1"
"@stimulus/core" "^2.0.0"
"@stimulus/webpack-helpers" "^2.0.0"
store2@^2.7.1:
version "2.12.0"