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

Add rescript example (#442) #552

Merged
merged 30 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
075856e
Add rescript example (#442)
Yassa-hue Aug 23, 2023
94e2a71
Solve render comments issue
Yassa-hue Aug 24, 2023
fb24f60
Refactor css transaction components
Yassa-hue Aug 28, 2023
3fe89de
Ignore the rescript output js files from lint
Yassa-hue Aug 28, 2023
bf38c2c
Apply review requested changes
Yassa-hue Sep 4, 2023
67ea623
Remove unused tag
Yassa-hue Sep 4, 2023
147f07e
Add ROR binding
Yassa-hue Sep 4, 2023
a54627e
Apply review requested changes
Yassa-hue Sep 7, 2023
b3ad543
remove action js file
Yassa-hue Sep 8, 2023
0b62d78
Remove .mjs files
Yassa-hue Sep 8, 2023
51a41d9
Compile rescript files before running js tests
Yassa-hue Sep 8, 2023
32e632d
Build recript files before test
Yassa-hue Sep 8, 2023
24e6a6a
minor change
Yassa-hue Oct 5, 2023
26f9bde
remove react-on-rails binding
Yassa-hue Oct 18, 2023
7379211
migrate to tailwind
Yassa-hue Oct 19, 2023
c259cbb
remove bs.mjs files
Yassa-hue Oct 19, 2023
7bde5fc
remove unused scss files
Yassa-hue Oct 19, 2023
49da88e
fix rescript spec tests
Yassa-hue Oct 19, 2023
3af072d
build rescript before test
Yassa-hue Oct 19, 2023
310fdb5
refactor rescript src
Yassa-hue Nov 1, 2023
ef879b1
fix add commit fail bug
Yassa-hue Nov 2, 2023
9a848f9
refactor the types
Yassa-hue Nov 3, 2023
e7bde79
format rescript files
Yassa-hue Nov 3, 2023
e328420
remove the types files
Yassa-hue Nov 3, 2023
b50e9e3
update error rescript page state design
Yassa-hue Nov 10, 2023
9b8e357
refactor switch statements
Yassa-hue Nov 13, 2023
1a22a45
move store comment state to the form component
Yassa-hue Nov 14, 2023
0edbefd
format rescript
Yassa-hue Nov 14, 2023
7ff8506
rename the saving state
Yassa-hue Nov 15, 2023
4b10773
empty commit
Yassa-hue Nov 16, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ public/
client/app/libs/i18n/translations.js
client/app/libs/i18n/default.js
postcss.config.js
client/app/bundles/comments/rescript/
3 changes: 3 additions & 0 deletions .github/workflows/rspec_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ jobs:
- name: Build i18n libraries
run: bundle exec rake react_on_rails:locale

- name: Build Rescript components
run: yarn res:build

- name: Build shakapacker chunks
run: NODE_ENV=development bundle exec bin/shakapacker

Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,8 @@ client/app/libs/i18n/default.js
/yarn-error.log
yarn-debug.log*
.yarn-integrity

lib/bs
/lib/ocaml

client/app/bundles/comments/rescript/**/*.bs.js
1 change: 1 addition & 0 deletions Procfile.dev
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Procfile for development using HMR
# You can run these commands in separate shells
rescript: yarn res:dev
redis: redis-server
rails: bundle exec rails s -p 3000
wp-client: HMR=true RAILS_ENV=development NODE_ENV=development bin/shakapacker-dev-server
Expand Down
2 changes: 2 additions & 0 deletions app/controllers/pages_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ def no_router

def simple; end

def rescript; end

private

def set_comments
Expand Down
1 change: 1 addition & 0 deletions app/views/pages/rescript.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= react_component "RescriptShow", prerender: true %>
28 changes: 28 additions & 0 deletions bsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "react-webpack-rails-tutorial",
"sources": [
{
"dir": "client/app/bundles/comments/rescript",
"subdirs": true
}
],
"package-specs": [
{
"module": "es6",
"in-source": true
}
],
"bsc-flags": ["-open JsonCombinators", "-open Belt"],
"suffix": ".bs.js",
"bs-dependencies": [
"@rescript/react",
"@rescript/core",
"@glennsl/rescript-fetch",
"@glennsl/rescript-json-combinators",
"rescript-react-on-rails"
],
"jsx": {
"version": 4,
"mode": "automatic"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ function NavigationBar(props) {
Classic Rails
</a>
</li>
<li className={classNames({ 'bg-yellow-100': pathname === paths.RESCRIPT_PATH })}>
<a
className="px-2 py-4 w-full inline-block text-gray-500 hover:text-gray-700"
href={paths.RESCRIPT_PATH}
>
Rescript
</a>
</li>
<li>
<a
className="px-2 py-4 w-full inline-block text-gray-500 hover:text-gray-700"
Expand Down
1 change: 1 addition & 0 deletions client/app/bundles/comments/constants/paths.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const ROUTER_PATH = '/';
export const REACT_ROUTER_PATH = '/react-router';
export const NO_ROUTER_PATH = '/no-router';
export const RESCRIPT_PATH = '/rescript';
export const SIMPLE_REACT_PATH = '/simple';
export const STIMULUS_PATH = '/stimulus';
export const RAILS_PATH = '/comments';
59 changes: 59 additions & 0 deletions client/app/bundles/comments/rescript/Actions/Actions.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// TODO : use only one way to make http requests either Axios or Fetch
module Create = {
type t = {
author: string,
text: string,
}

let storeComment = async (comment: t) => {
let _ = await Axios.post(
"comments.json",
{
"author": comment.author,
"text": comment.text,
},
{
"responseType": "json",
"headers": {
// see https://github.com/shakacode/react_on_rails/blob/249c69812474e0f532df432581bf5e618df0e1ec/node_package/src/Authenticity.ts#L13C1-L18C1
"X-CSRF-Token": ReactOnRails.authenticityToken(),
"X-Requested-With": "XMLHttpRequest",
},
},
)
}
}

module Fetch = {
type t = {
author: string,
text: string,
id: int,
}

type comments = array<t>

type commentsRes = {comments: comments}

let fetchComments = async (): result<comments, string> => {
open Json.Decode

let response = await Fetch.get("comments.json")
let jsonRes = await response->Fetch.Response.json

let jsonComment = Json.Decode.object(field => {
author: field.required(. "author", string),
text: field.required(. "text", string),
id: field.required(. "id", int),
})

let jsonComments = Json.Decode.object(field => {
comments: field.required(. "comments", array(jsonComment)),
})

switch jsonRes->Json.decode(jsonComments) {
| Ok(decodedRes) => Ok(decodedRes.comments)
| Error(e) => Error(e)
}
}
}
149 changes: 149 additions & 0 deletions client/app/bundles/comments/rescript/CommentForm/CommentForm.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
type formDisplay =
| Horizontal
| Inline
| Stacked

type savingStatus =
| Idle
| Saving
| Error

type formData = {
formName: string,
formType: formDisplay,
}

type state = {
author: string,
text: string,
form: formDisplay,
savingStatus: savingStatus,
}

type action =
| SetAuthor(string)
| SetText(string)
| SetFormType(formDisplay)
| SetSavingError
| ClearSavingError
| SetStoreStatusSaving

let reducer = (state: state, action: action): state => {
switch action {
| SetAuthor(author) => {...state, author}
| SetText(text) => {...state, text}
| SetFormType(form) => {...state, form}
| SetSavingError => {...state, savingStatus: Error}
| ClearSavingError => {...state, savingStatus: Idle}
| SetStoreStatusSaving => {...state, savingStatus: Saving}
}
}

@react.component
let make = (~fetchData) => {
let (state, dispatch) = React.useReducer(
reducer,
{
author: "",
text: "",
form: Horizontal,
savingStatus: Idle,
},
)

let disabled = React.useMemo1(() => {
switch state.savingStatus {
| Saving => true
| Idle
| Error => false
}
}, [state.savingStatus])

let storeComment = (newComment: Actions.Create.t) => {
SetStoreStatusSaving->dispatch
let saveAndFetchComments = async () => {
try {
let _ = await Actions.Create.storeComment(newComment)
ClearSavingError->dispatch

await fetchData()
} catch {
| _ => SetSavingError->dispatch
}
}
saveAndFetchComments()->ignore
}

let handleAuthorChange = event => {
let value = ReactEvent.Form.currentTarget(event)["value"]
SetAuthor(value)->dispatch
}

let handleTextChange = event => {
let value = ReactEvent.Form.currentTarget(event)["value"]
SetText(value)->dispatch
}

let handleSubmit = event => {
ReactEvent.Form.preventDefault(event)
storeComment({author: state.author, text: state.text})
}

let forms: array<formData> = [
{formName: "Horizontal Form", formType: Horizontal},
{formName: "Inline Form", formType: Inline},
{formName: "Stacked Form", formType: Stacked},
]

<div>
<div className="flex gap-1 not-prose">
{forms
->Array.map(form =>
<button
key={`form_${form.formName}`}
className={`px-6 py-2 font-semibold border-0 rounded ${state.form == form.formType
? "text-sky-50 bg-sky-600"
: "text-sky-600 hover:bg-gray-100"}`}
onClick={event => SetFormType(form.formType)->dispatch}>
{form.formName->React.string}
</button>
)
->React.array}
</div>
<hr />
{switch state.form {
| Horizontal =>
<HorizontalForm
author={state.author}
handleAuthorChange
text={state.text}
handleTextChange
handleSubmit
disabled
/>
| Stacked =>
<StackedFrom
author={state.author}
handleAuthorChange
text={state.text}
handleTextChange
handleSubmit
disabled
/>
| Inline =>
<InlineForm
author={state.author}
handleAuthorChange
text={state.text}
handleTextChange
handleSubmit
disabled
/>
}}
{switch state.savingStatus {
| Error => <AlertError errorMsg="Can't save the comment!" />
| Idle
| Saving => React.null
}}
</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@react.component
let make = (~author, ~handleAuthorChange, ~text, ~handleTextChange, ~handleSubmit, ~disabled) => {
<form className="form-horizontal flex flex-col gap-4" onSubmit=handleSubmit disabled>
<div className="flex flex-col gap-0 items-center lg:gap-4 lg:flex-row">
<label className="w-full lg:w-2/12 lg:text-end shrink-0"> {"Name"->React.string} </label>
<input
type_="text"
className="px-3 py-1 leading-4 border border-gray-300 rounded w-full"
placeholder="Your Name"
name="comment_author"
id="comment_author"
onChange=handleAuthorChange
value={author}
/>
</div>
<div className="flex flex-col gap-0 items-center lg:gap-4 lg:flex-row">
<label className="w-full lg:w-2/12 lg:text-end shrink-0"> {"Text"->React.string} </label>
<input
type_="text"
className="px-3 py-1 leading-4 border border-gray-300 rounded w-full"
placeholder="Say something using markdown..."
name="comment_text"
id="comment_text"
onChange=handleTextChange
value={text}
/>
</div>
<div className="flex flex-col gap-0 lg:gap-4 lg:flex-row">
<div className="hidden lg:block lg:w-2/12 grow-0" />
<input
type_="submit"
className="self-start px-3 py-1 font-semibold border-0 rounded text-sky-50 bg-sky-600 hover:bg-sky-800"
value="Post"
/>
</div>
</form>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
@react.component
let make = (~author, ~handleAuthorChange, ~text, ~handleTextChange, ~handleSubmit, ~disabled) => {
<form
className="form-inline flex flex-col lg:flex-row flex-wrap gap-4"
onSubmit=handleSubmit
disabled>
<div className="flex gap-2 items-center">
<label> {"Name"->React.string} </label>
<input
type_="text"
className="px-3 py-1 leading-4 border border-gray-300 rounded"
placeholder="Your Name"
name="comment_author"
id="comment_author"
value={author}
onChange=handleAuthorChange
/>
</div>
<div className="flex gap-2 items-center">
<label> {"Text"->React.string} </label>
<input
type_="text"
className="px-3 py-1 leading-4 border border-gray-300 rounded"
placeholder="Say something using markdown..."
name="comment_text"
id="comment_text"
onChange=handleTextChange
value={text}
/>
</div>
<div className="flex gap-2">
<input
type_="submit"
className="self-start px-3 py-1 font-semibold border-0 rounded text-sky-50 bg-sky-600 hover:bg-sky-800"
onSubmit=handleSubmit
value="Post"
/>
</div>
</form>
}
Loading