-
Notifications
You must be signed in to change notification settings - Fork 373
demo react-over-hotwired and proof #1508 fixed #592
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
base: master
Are you sure you want to change the base?
Changes from 3 commits
6194793
f3687a4
1983eb5
4f7480b
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,30 @@ | ||
| <%= form_for(@comment, html: { class: "flex flex-col gap-4" }) do |f| %> | ||
| <% if @comment.errors.any? %> | ||
| <div id="error_explanation" class="prose bg-pink-100 p-4 mb-4 border border-pink-200 rounded text-red-800 prose-strong:text-red-800 prose-ul:my-1"> | ||
| <h2><%= pluralize(@comment.errors.count, "error") %> prohibited this comment from being saved:</h2> | ||
|
|
||
| <ul> | ||
| <% @comment.errors.full_messages.each do |message| %> | ||
| <li><%= message %></li> | ||
| <% end %> | ||
| </ul> | ||
| </div> | ||
| <% end %> | ||
|
|
||
| <div class="field"> | ||
| <%= f.label :author, 'Your Name' %><br> | ||
| <%= f.text_field :author, class: "px-3 py-1 leading-4 border border-gray-300 rounded" %> | ||
| </div> | ||
| <div class="field"> | ||
| <%= f.label :text, 'Say something using markdown...' %><br> | ||
| <%= f.text_area :text, class: "px-3 py-1 leading-4 border border-gray-300 rounded" %> | ||
| </div> | ||
| <div class="actions"> | ||
| <%= f.submit 'Post', class: "self-start px-3 py-1 font-semibold border-0 rounded text-sky-50 bg-sky-600 hover:bg-sky-800 cursor-pointer" %> | ||
| </div> | ||
|
|
||
| <h1>Below is a react component which render inside a form (that will proof #1508 is fixed), You can turn-off the flag `force_load` and this below form will not show. </h1> | ||
| <div class="mx-10 border border-red-600 rounded"> | ||
| <%= react_component('HotwiredCommentForm', props: { }, prerender: false, force_load: true) %> | ||
| </div> | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| <% end %> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| <%= turbo_stream.prepend "comments" do %> | ||
| <div> | ||
| <h2 class="text-blue-800"><%= @comment.author %></h2> | ||
| <span><%= markdown_to_html(@comment.text) %></span> | ||
| </div> | ||
| <% end %> | ||
|
Comment on lines
+1
to
+6
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. Don't prepend into a React-owned list.
🤖 Prompt for AI Agents |
||
|
|
||
| <%= turbo_stream.update "comment-form" do %> | ||
| <%= link_to "New Comment", new_comment_path, data: { turbo_stream: true } %> | ||
| <% end %> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| <%= turbo_stream.update "comment-form" do %> | ||
| <div class="p-5 m-5 border border-red-600 rounded"> | ||
| <h2>New Comment</h2> | ||
|
|
||
| <%= react_component("HotwiredCommentForm", props: {}, prerender: false, force_load: true) %> | ||
| </div> | ||
| <% end %> |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,3 @@ | ||||||
| <%= turbo_stream.replace dom_id(@comment), target: "_top" do %> | ||||||
|
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. 🧩 Analysis chain🌐 Web query:
💡 Result: The turbo_stream.replace helper in turbo-rails supports the following main options, based on official documentation and source code: - target: Required DOM ID (string, model/record, or array) of the element to replace. - Rendering options for content: partial:, locals: {}, block content &block, or direct content string/HTML. - method: :morph (or "morph" string): Uses morphing instead of direct replacement/innerHTML for the replace action. Examples from docs: <%= turbo_stream.replace "clearance_5", " Replace... " %> <%= turbo_stream.replace clearance, partial: "clearances/clearance", locals: { title: "Hello" } %> <%= turbo_stream.replace clearance, "Morph the dom target ", method: :morph %> Additional attributes like attributes can be passed to the underlying tag via turbo_stream_action_tag, but _top is not a supported option for turbo_stream.replace. The _top value is a special data-turbo-frame target for Turbo Frames (e.g., data: { turbo_frame: "_top" } on links/forms), used to break out of nested frames and navigate the top-level page. It has no meaning or support in Turbo Streams actions like replace, which target arbitrary DOM elements by ID/CSS via target/targets attributes, independent of frame navigation.
Citations:
Remove unsupported Line 1 passes ♻️ Suggested fix-<%= turbo_stream.replace dom_id(`@comment`), target: "_top" do %>
+<%= turbo_stream.replace dom_id(`@comment`) do %>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| <%= render partial: "form_1508" %> | ||||||
| <% end %> | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| <h2>Demo React Over Hotwired</h2> | ||
|
|
||
| <%= react_component("HotwiredCommentScreen", props: @props, prerender: false) %> | ||
|
|
||
| <%= turbo_frame_tag "new_comment" do %> | ||
| <%= link_to "proof that #1508 fixed", "/test_1508", data: { turbo_stream: true } %> | ||
| <% end %> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| .elementEnter { | ||
| opacity: 0.01; | ||
|
|
||
| &.elementEnterActive { | ||
| opacity: 1; | ||
| transition: opacity $animation-duration ease-in; | ||
| } | ||
| } | ||
|
|
||
| .elementLeave { | ||
| opacity: 1; | ||
|
|
||
| &.elementLeaveActive { | ||
| opacity: 0.01; | ||
| transition: opacity $animation-duration ease-in; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,111 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // eslint-disable-next-line max-classes-per-file | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import React from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import request from 'axios'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import _ from 'lodash'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import ReactOnRails from 'react-on-rails'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { IntlProvider, injectIntl } from 'react-intl'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import BaseComponent from 'libs/components/BaseComponent'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import SelectLanguage from 'libs/i18n/selectLanguage'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { defaultLocale } from 'libs/i18n/default'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { translations } from 'libs/i18n/translations'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { Turbo } from '@hotwired/turbo-rails'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import CommentForm from '../../CommentBox/CommentForm/CommentForm'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import css from '../HotwiredCommentScreen.module.scss'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class HotwiredCommentForm extends BaseComponent { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| constructor(props) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| super(props); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.state = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isSaving: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| submitCommentError: null, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _.bindAll(this, 'handleCommentSubmit'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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. Consider using arrow functions for method bindings instead of lodash's Arrow functions can make the code cleaner and reduce the dependency on lodash for this purpose. Here's how you can refactor the handleCommentSubmit = (comment) => {
// method body remains the same
} |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleCommentSubmit(comment) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.setState({ isSaving: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const requestConfig = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| responseType: 'text/vnd.turbo-stream.html', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| headers: ReactOnRails.authenticityHeaders(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+32
to
+34
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. 🧩 Analysis chain🌐 Web query:
💡 Result: In Axios 0.21.1, the valid responseType values are 'arraybuffer', 'document', 'json', 'text', 'stream'. In browsers, 'blob' is also valid. text/vnd.turbo-stream.html is not a valid responseType. It is a content type that should be requested via the Accept header in the request config (e.g., headers: { Accept: 'text/vnd.turbo-stream.html' }) to instruct the server to return that format. Use responseType: 'text' to receive the response body as a string for manual parsing if needed. Citations:
Axios/XHR expects Proposed fix const requestConfig = {
- responseType: 'text/vnd.turbo-stream.html',
- headers: ReactOnRails.authenticityHeaders(),
+ responseType: 'text',
+ headers: {
+ ...ReactOnRails.authenticityHeaders(),
+ Accept: 'text/vnd.turbo-stream.html',
+ },
};🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return request | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .post('/comments.turbo_stream', { comment }, requestConfig) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .then(r => r.data) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .then(html => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Turbo.renderStreamMessage(html); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .then(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.setState({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| submitCommentError: null, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isSaving: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .catch((error) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.setState({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| submitCommentError: error, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isSaving: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+49
to
+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. Re-throw failed submits so the draft is not cleared.
♻️ Suggested fix .catch((error) => {
this.setState({
submitCommentError: error,
isSaving: false,
});
+ throw error;
});🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { handleSetLocale, locale } = this.props; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const cssTransitionGroupClassNames = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| enter: css.elementEnter, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| enterActive: css.elementEnterActive, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit: css.elementLeave, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exitActive: css.elementLeaveActive, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="commentBox prose max-w-none prose-a:text-sky-700 prose-li:my-0"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {SelectLanguage(handleSetLocale, locale)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <CommentForm | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isSaving={this.state.isSaving} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| actions={{ submitComment: this.handleCommentSubmit }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error={{ error: this.state.submitCommentError, nodeRef: React.createRef(null) }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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. 🧩 Analysis chain🏁 Script executed: fd "HotwiredCommentForm.jsx" --type fRepository: shakacode/react-webpack-rails-tutorial Length of output: 181 🏁 Script executed: cat -n client/app/bundles/comments/components/HotwiredCommentScreen/ror_components/HotwiredCommentForm.jsxRepository: shakacode/react-webpack-rails-tutorial Length of output: 3866 🏁 Script executed: fd "CommentForm.jsx" --type f | grep -E "CommentBox|CommentForm"Repository: shakacode/react-webpack-rails-tutorial Length of output: 259 🏁 Script executed: cat -n client/app/bundles/comments/components/CommentBox/CommentForm/CommentForm.jsxRepository: shakacode/react-webpack-rails-tutorial Length of output: 14891 Do not create a new Line 72 recreates the ref on each render, which breaks error message animations in the CSSTransition component. The Proposed fix constructor(props) {
super(props);
this.state = {
isSaving: false,
submitCommentError: null,
};
+ this.errorNodeRef = React.createRef();
_.bindAll(this, 'handleCommentSubmit');
}
@@
<CommentForm
isSaving={this.state.isSaving}
actions={{ submitComment: this.handleCommentSubmit }}
- error={{ error: this.state.submitCommentError, nodeRef: React.createRef(null) }}
+ error={{ error: this.state.submitCommentError, nodeRef: this.errorNodeRef }}
cssTransitionGroupClassNames={cssTransitionGroupClassNames}
/>🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cssTransitionGroupClassNames={cssTransitionGroupClassNames} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+57
to
+75
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. Pass
♻️ Suggested fix render() {
- const { handleSetLocale, locale } = this.props;
+ const { handleSetLocale, locale, intl } = this.props;
@@
<CommentForm
isSaving={this.state.isSaving}
actions={{ submitComment: this.handleCommentSubmit }}
error={{ error: this.state.submitCommentError, nodeRef: React.createRef(null) }}
cssTransitionGroupClassNames={cssTransitionGroupClassNames}
+ intl={intl}
/>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default class I18nWrapper extends BaseComponent { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| constructor(props) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| super(props); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.state = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| locale: defaultLocale, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+85
to
+87
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. Initialize locale from props to avoid first-render mismatch. Line 85 always uses Proposed fix this.state = {
- locale: defaultLocale,
+ locale: props.locale || defaultLocale,
};🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _.bindAll(this, 'handleSetLocale'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleSetLocale(locale) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.setState({ locale }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { locale } = this.state; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const messages = translations[locale]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const InjectedHotwiredCommentForm = injectIntl(HotwiredCommentForm); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <IntlProvider locale={locale} key={locale} messages={messages}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <InjectedHotwiredCommentForm | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* eslint-disable-next-line react/jsx-props-no-spreading */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {...this.props} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| locale={locale} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleSetLocale={this.handleSetLocale} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </IntlProvider> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,84 @@ | ||||||||||||||
| // eslint-disable-next-line max-classes-per-file | ||||||||||||||
| import React from 'react'; | ||||||||||||||
| import Immutable from 'immutable'; | ||||||||||||||
| import _ from 'lodash'; | ||||||||||||||
| import { IntlProvider, injectIntl } from 'react-intl'; | ||||||||||||||
| import BaseComponent from 'libs/components/BaseComponent'; | ||||||||||||||
| import SelectLanguage from 'libs/i18n/selectLanguage'; | ||||||||||||||
| import { defaultMessages, defaultLocale } from 'libs/i18n/default'; | ||||||||||||||
| import { translations } from 'libs/i18n/translations'; | ||||||||||||||
|
|
||||||||||||||
| import CommentList from '../../CommentBox/CommentList/CommentList'; | ||||||||||||||
| import css from '../HotwiredCommentScreen.module.scss'; | ||||||||||||||
|
|
||||||||||||||
| class HotwiredCommentScreen extends BaseComponent { | ||||||||||||||
| constructor(props) { | ||||||||||||||
| super(props); | ||||||||||||||
|
|
||||||||||||||
| this.state = { | ||||||||||||||
| $$comments: Immutable.fromJS(props.comments), | ||||||||||||||
| }; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| render() { | ||||||||||||||
| const { handleSetLocale, locale, intl } = this.props; | ||||||||||||||
| const { formatMessage } = intl; | ||||||||||||||
| const cssTransitionGroupClassNames = { | ||||||||||||||
| enter: css.elementEnter, | ||||||||||||||
| enterActive: css.elementEnterActive, | ||||||||||||||
| exit: css.elementLeave, | ||||||||||||||
| exitActive: css.elementLeaveActive, | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| return ( | ||||||||||||||
| <div className="commentBox prose max-w-none prose-a:text-sky-700 prose-li:my-0"> | ||||||||||||||
| <turbo-frame id="comment-box"> | ||||||||||||||
| <h2>{formatMessage(defaultMessages.comments)}</h2> | ||||||||||||||
| {SelectLanguage(handleSetLocale, locale)} | ||||||||||||||
|
|
||||||||||||||
| <CommentList | ||||||||||||||
| $$comments={this.state.$$comments} | ||||||||||||||
| cssTransitionGroupClassNames={cssTransitionGroupClassNames} | ||||||||||||||
| /> | ||||||||||||||
|
|
||||||||||||||
| <div id="comment-form"> | ||||||||||||||
| <a data-turbo-stream="true" href="/comments/new">New Comment</a> | ||||||||||||||
| </div> | ||||||||||||||
| </turbo-frame> | ||||||||||||||
| </div> | ||||||||||||||
| ); | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| export default class I18nWrapper extends BaseComponent { | ||||||||||||||
| constructor(props) { | ||||||||||||||
| super(props); | ||||||||||||||
|
|
||||||||||||||
| this.state = { | ||||||||||||||
| locale: defaultLocale, | ||||||||||||||
| }; | ||||||||||||||
|
Comment on lines
+58
to
+60
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. Use incoming locale prop for initial state. Line 58 always starts from Proposed fix export default class I18nWrapper extends BaseComponent {
constructor(props) {
super(props);
this.state = {
- locale: defaultLocale,
+ locale: props.locale || defaultLocale,
};
_.bindAll(this, 'handleSetLocale');
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
|
|
||||||||||||||
| _.bindAll(this, 'handleSetLocale'); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| handleSetLocale(locale) { | ||||||||||||||
| this.setState({ locale }); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| render() { | ||||||||||||||
| const { locale } = this.state; | ||||||||||||||
| const messages = translations[locale]; | ||||||||||||||
| const InjectedHotwiredCommentScreen = injectIntl(HotwiredCommentScreen); | ||||||||||||||
|
|
||||||||||||||
| return ( | ||||||||||||||
| <IntlProvider locale={locale} key={locale} messages={messages}> | ||||||||||||||
| <InjectedHotwiredCommentScreen | ||||||||||||||
| /* eslint-disable-next-line react/jsx-props-no-spreading */ | ||||||||||||||
| {...this.props} | ||||||||||||||
| locale={locale} | ||||||||||||||
| handleSetLocale={this.handleSetLocale} | ||||||||||||||
| /> | ||||||||||||||
| </IntlProvider> | ||||||||||||||
| ); | ||||||||||||||
|
Comment on lines
+69
to
+83
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. Hoist Calling Proposed fix+const InjectedHotwiredCommentScreen = injectIntl(HotwiredCommentScreen);
+
export default class I18nWrapper extends BaseComponent {
constructor(props) {
super(props);
this.state = {
- locale: defaultLocale,
+ locale: props.locale || defaultLocale,
};
_.bindAll(this, 'handleSetLocale');
}
handleSetLocale(locale) {
this.setState({ locale });
}
render() {
const { locale } = this.state;
const messages = translations[locale];
- const InjectedHotwiredCommentScreen = injectIntl(HotwiredCommentScreen);
return (
<IntlProvider locale={locale} key={locale} messages={messages}>
<InjectedHotwiredCommentScreen
/* eslint-disable-next-line react/jsx-props-no-spreading */
{...this.props}
locale={locale}
handleSetLocale={this.handleSetLocale}
/>
</IntlProvider>
);
}
}🤖 Prompt for AI Agents |
||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
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.
Mirror the existing
render_to_stringresponse handling here.Line 42 calls
comments_json_string, which usesrender_to_string. The controller comment above and theindex/no_routeractions both show that an explicit HTML response is needed afterward. Withoutrender_html,pages#hotwiredcan render/negotiation-handle differently from the other controller-driven pages.♻️ Suggested fix
def hotwired `@props` = comments_json_string + render_html end📝 Committable suggestion
🤖 Prompt for AI Agents