-
Notifications
You must be signed in to change notification settings - Fork 426
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 the beginnings of a Preact create-group form #8789
Conversation
adb082c
to
ad65d76
Compare
.babelrc
Outdated
"presets": [ | ||
"@babel/preset-typescript", | ||
[ | ||
"@babel/preset-react", | ||
{ | ||
"runtime": "automatic", | ||
"importSource": "preact" | ||
} | ||
], | ||
"@babel/preset-env" | ||
], | ||
"env": { | ||
"development": { | ||
"presets": [ | ||
[ | ||
"@babel/preset-react", | ||
{ | ||
"development": true, | ||
"runtime": "automatic", | ||
"importSource": "preact" | ||
} | ||
] | ||
] | ||
} | ||
} |
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.
This is copied from Via: https://github.com/hypothesis/via/blob/2f9e78063cb0e4fc731915f2f4c5f791da1c7467/.babelrc#L1-L34
Via also passes "bugfixes": true
to preset-env
but that seems separate from the issue of adding our Preact + Tailwind stack so I've not added that.
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.
I'm fine if you want to make that a separate PR, although I think this is a pretty safe option to add as we've been using it a long time. The documentation mentions that it will become the default option in future Babel releases, so we probably ought to be using it everywhere.
Related to this, we need to get our browserslist updated everywhere to something a bit more modern.
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.
Done: c204e69
h/static/scripts/tsconfig.json
Outdated
{ | ||
"compilerOptions": { | ||
"allowJs": true, | ||
"allowSyntheticDefaultImports": true, | ||
"checkJs": true, | ||
"lib": ["es2018", "dom"], | ||
"jsx": "react-jsx", | ||
"jsxImportSource": "preact", | ||
"module": "commonjs", | ||
"noEmit": true, | ||
"strict": true, | ||
"target": "ES2020", | ||
"useUnknownInCatchVariables": false | ||
}, | ||
"include": ["**/*.js", "**/*.ts", "**/*.tsx"], | ||
"exclude": [ | ||
// Tests and test infrastructure | ||
"**/test/*.js", | ||
"bootstrap.js", | ||
"karma.config.cjs" | ||
] | ||
} |
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.
tailwind.config.js
Outdated
import tailwindConfig from '@hypothesis/frontend-shared/lib/tailwind.preset.js'; | ||
|
||
export default { | ||
presets: [tailwindConfig], | ||
content: [ | ||
'./h/static/scripts/**/*.{js,ts,tsx}', | ||
'./node_modules/@hypothesis/frontend-shared/lib/**/*.js', | ||
], | ||
theme: { | ||
extend: { | ||
animation: { | ||
'fade-in': 'fade-in 0.3s forwards', | ||
'fade-out': 'fade-out 0.3s forwards', | ||
'slide-in-from-right': 'slide-in-from-right 0.3s forwards ease-in-out', | ||
}, | ||
fontFamily: { | ||
sans: [ | ||
'"Helvetica Neue"', | ||
'Helvetica', | ||
'Arial', | ||
'"Lucida Grande"', | ||
'sans-serif', | ||
], | ||
}, | ||
boxShadow: { | ||
// Similar to tailwind's default `shadow-inner` but coming from the | ||
// right edge instead of the left | ||
'r-inner': 'inset -2px 0 4px 0 rgb(0,0,0,.05)', | ||
}, | ||
keyframes: { | ||
'fade-in': { | ||
'0%': { | ||
opacity: '0', | ||
}, | ||
'100%': { | ||
opacity: '1', | ||
}, | ||
}, | ||
'fade-out': { | ||
'0%': { | ||
opacity: '1', | ||
}, | ||
'100%': { | ||
opacity: '0', | ||
}, | ||
}, | ||
'slide-in-from-right': { | ||
'0%': { | ||
opacity: '0', | ||
left: '100%', | ||
}, | ||
'80%': { | ||
left: '-10px', | ||
}, | ||
'100%': { | ||
left: '0', | ||
opacity: '1', | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}; |
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.
Copied from Via's tailwind.config.js
. I'm not sure if we need the theme stuff in h
); | ||
|
||
gulp.task('watch-css', () => { | ||
gulp.watch( | ||
'h/static/styles/**/*.scss', | ||
['h/static/styles/**/*.{css,scss}', 'h/static/scripts/**/*.{js,ts,tsx}'], |
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.
"@babel/preset-react": "^7.24.7", | ||
"@babel/preset-typescript": "^7.24.7", |
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.
All the packages I'm adding are copied from Via's package.json
. I set the minimum version numbers of all the packages I added to the current latest versions on NPM, which means some of them are newer than the minimum versions given in Via, may as well start h off on the latest versions (I don't think this actually makes any difference to the compiled requirements anyway).
@view_defaults( | ||
route_name="group_create", | ||
renderer="h:templates/groups/create.html.jinja2", | ||
is_authenticated=True, | ||
feature="preact_create_group_form", | ||
) | ||
class GroupCreateController: | ||
def __init__(self, request): | ||
self.request = request | ||
|
||
@view_config(request_method="GET") | ||
def get(self): | ||
"""Render the page for creating a new group.""" | ||
return {} |
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.
Starting fresh with a completely new controller for the new version of the page.
h/assets.ini
Outdated
|
||
create_group_form_css = | ||
styles/create-group-form.css | ||
|
||
create_group_form_js = | ||
scripts/create-group-form.bundle.js |
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.
Adding the asset bundles for my new "create group form" app, same as Via does for its video player app: https://github.com/hypothesis/via/blob/2f9e78063cb0e4fc731915f2f4c5f791da1c7467/via/assets.ini#L3-L7
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.
I expect we'll want to make this a bit more general in future so we have a bundle for all the new UI rather than just the "create group" UI. Only once that gets large enough would we then need to split it up again.
<div id="create-group-form"></div> | ||
{% endblock %} |
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.
Replacing the legacy form with our Preact app. The group.html.jinja2
base template renders this inside a couple of container divs (<div class="content paper"><div class="form-container">
):
h/h/templates/layouts/group.html.jinja2
Lines 9 to 15 in b29dd69
{% block content %} | |
<div class="content paper"> | |
<div class="form-container"> | |
{% include "h:templates/includes/flash-messages.html.jinja2" %} | |
{{ self.page_content() }} | |
</div> | |
</div> |
It also adds flash messages to the page, which we might not want here..
{% extends "h:templates/layouts/group.html.jinja2" %} | ||
|
||
{% set page_title = 'Create a new private group' %} | ||
|
||
{% block page_content %} | ||
<h1 class="form-header">Create a new private group</h1> | ||
{{ form }} | ||
|
||
<footer class="form-footer"> | ||
{# This form has at least one required field. If that changes we should update this footer. #} | ||
<span class="form-footer__required"> | ||
<span class="form-footer__symbol"> | ||
* | ||
</span> | ||
<span class="form-footer__text"> | ||
Required | ||
</span> | ||
</span> | ||
</footer> | ||
{% endblock page_content %} |
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.
This is the previous create.html.jinja2
template, just renamed
<h1 class="form-header">Create a new private group</h1> | ||
|
||
<form class="form group-form__form"> | ||
<div class="form-input"> | ||
<label for="name" class="form-input__label group-form__name-label"> | ||
Name <span class="form-input__required">*</span> | ||
</label> | ||
<input | ||
type="text" | ||
id="name" | ||
class="form-input__input group-form__name-input has-label" | ||
autofocus="" | ||
autocomplete="off" | ||
required | ||
/> | ||
<span class="form-input__character-counter is-ready">0/25</span> | ||
</div> | ||
|
||
<div class="form-input"> | ||
<label | ||
class="form-input__label group-form__description-label" | ||
for="description" | ||
> | ||
Description | ||
</label> | ||
<textarea | ||
id="description" | ||
class="form-input__input group-form__description-input has-label" | ||
></textarea> | ||
<span class="form-input__character-counter is-ready">0/250</span> | ||
</div> | ||
|
||
<div class="form-actions"> | ||
<div class="u-stretch"></div> | ||
|
||
<div class="form-actions__buttons"> | ||
<button class="form-actions__btn btn primary-action-btn group-form__submit-btn"> | ||
Create group | ||
</button> | ||
</div> | ||
</div> | ||
</form> | ||
|
||
<footer class="form-footer"> | ||
<span class="form-footer__required"> | ||
<span class="form-footer__symbol">*</span> | ||
<span class="form-footer__text">Required</span> | ||
</span> | ||
</footer> |
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.
I've copied the HTML elements and CSS classes from the legacy create group form.
We're still including the legacy CSS (the site_css
bundle) because the parts of the legacy page surrounding the new group form are still in use (the Hypothesis logo in the top left, the HTML <head>
with various things in it, and the <body>
and some layout/container elements). Therefore, we can just use the legacy CSS classes from our Preact component!
This is a quick way to get a create group form that looks exactly like the legacy one.
It's very close but this doesn't end up looking exactly like the legacy form: the inclusion of @tailwind base
messes with the layout, changing some of the sizes/margins/paddings just slightly. This can be fixed by simply removing Tailwind (which is not being used).
Whether we actually want to go with this look in the end or not, I don't know yet.
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.
This looks good. There are a few things I noticed that can be simplified or updated. Regarding the TypeScript setup, I posted a minimal diff so you can get yarn typecheck
running locally. Feel free to do that now or it can be addressed after. We should get it running in CI soon though.
.babelrc
Outdated
"presets": [ | ||
"@babel/preset-typescript", | ||
[ | ||
"@babel/preset-react", | ||
{ | ||
"runtime": "automatic", | ||
"importSource": "preact" | ||
} | ||
], | ||
"@babel/preset-env" | ||
], | ||
"env": { | ||
"development": { | ||
"presets": [ | ||
[ | ||
"@babel/preset-react", | ||
{ | ||
"development": true, | ||
"runtime": "automatic", | ||
"importSource": "preact" | ||
} | ||
] | ||
] | ||
} | ||
} |
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.
I'm fine if you want to make that a separate PR, although I think this is a pretty safe option to add as we've been using it a long time. The documentation mentions that it will become the default option in future Babel releases, so we probably ought to be using it everywhere.
Related to this, we need to get our browserslist updated everywhere to something a bit more modern.
gulpfile.js
Outdated
buildCSS( | ||
[ | ||
'./node_modules/bootstrap/dist/css/bootstrap.css', | ||
'./h/static/styles/admin.scss', | ||
'./h/static/styles/help-page.scss', | ||
'./h/static/styles/site.scss', | ||
'./h/static/styles/vendor/icomoon.css', | ||
'./h/static/styles/create-group-form.css', | ||
], | ||
{ tailwindConfig }, | ||
), |
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.
Indeed. I would probably separate out the build for the Tailwind and non-Tailwind CSS. You can easily add two separate gulp tasks (build-legacy-css
, build-new-css
) and then make a build-css
task which runs them in parallel. See the gulpfile.js
in the client repo for an example of doing something like this.
h/assets.ini
Outdated
|
||
create_group_form_css = | ||
styles/create-group-form.css | ||
|
||
create_group_form_js = | ||
scripts/create-group-form.bundle.js |
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.
I expect we'll want to make this a bit more general in future so we have a bundle for all the new UI rather than just the "create group" UI. Only once that gets large enough would we then need to split it up again.
@@ -0,0 +1,63 @@ | |||
import { render } from 'preact'; |
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.
For Preact components the convention is to name the modules after the name of the main component and put them in a components
directory (with additional hierarchy as needed). Non-component modules use a kebab-case naming convention. Was this something you planned to do as part of making the UI actually functional?
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.
Done. I went with:
h/static/scripts/group-forms/index.tsx
-
h/static/scripts/group-forms/components/CreateGroupForm.tsx
h/static/styles/group-forms.css
assets.ini
bundle names:group_forms_css
andgroup_forms_js
h/static/scripts/tsconfig.json
Outdated
"allowJs": true, | ||
"allowSyntheticDefaultImports": true, | ||
"checkJs": true, | ||
"lib": ["es2018", "dom"], |
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.
This can safely be bumped to es2020
at least.
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.
Done: 7633256
h/static/scripts/tsconfig.json
Outdated
"lib": ["es2018", "dom"], | ||
"jsx": "react-jsx", | ||
"jsxImportSource": "preact", | ||
"module": "commonjs", |
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.
The module
configuration differs across repos. We probably should be using es2020
everywhere, which is the configuration in the client. If making that change doesn't cause any errors, I'd do it now, otherwise we can revisit later.
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.
Done: f7f6b97
h/static/scripts/tsconfig.json
Outdated
"target": "ES2020", | ||
"useUnknownInCatchVariables": false | ||
}, | ||
"include": ["**/*.js", "**/*.ts", "**/*.tsx"], |
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.
"include": ["**/*.js", "**/*.ts", "**/*.tsx"], | |
"include": ["**/*.ts", "**/*.tsx"], |
Limit type checking to TS / TSX files for now. That will make it easier to turn on typechecking in CI. You can also remove the exclude
list as well.
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.
Done: 44f0507
package.json
Outdated
"checkformatting": "prettier --check *.js h/static/scripts", | ||
"format": "prettier --list-different --write *.js h/static/scripts", | ||
"checkformatting": "prettier --check *.{js,ts,tsx} h/static/scripts", | ||
"format": "prettier --list-different --write *.{js,ts,tsx} h/static/scripts", | ||
"lint": "eslint h/static/scripts", | ||
"test": "gulp test" |
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.
To run TS, we'd add a script like:
"typecheck": "tsc --build h/static/scripts/tsconfig.json"
To make that command run successfully there are a few minor corrections needed. The most notable one is that there are some things in JSX that are slightly different from HTML, and TS can catch these for you.
diff --git a/h/static/scripts/create-group-form.tsx b/h/static/scripts/create-group-form.tsx
index 4393d90de..f858a8ca4 100644
--- a/h/static/scripts/create-group-form.tsx
+++ b/h/static/scripts/create-group-form.tsx
@@ -14,8 +14,8 @@ function CreateGroupForm() {
type="text"
id="name"
class="form-input__input group-form__name-input has-label"
- autofocus=""
- autocomplete="off"
+ autoFocus
+ autoComplete="off"
required
/>
<span class="form-input__character-counter is-ready">0/25</span>
@@ -57,7 +57,7 @@ function CreateGroupForm() {
}
function init() {
- render(<CreateGroupForm />, document.querySelector('#create-group-form'));
+ render(<CreateGroupForm />, document.querySelector('#create-group-form')!);
}
init();
diff --git a/h/static/scripts/tsconfig.json b/h/static/scripts/tsconfig.json
index 33fd708ba..2aadc5b43 100644
--- a/h/static/scripts/tsconfig.json
+++ b/h/static/scripts/tsconfig.json
@@ -12,7 +12,7 @@
"target": "ES2020",
"useUnknownInCatchVariables": false
},
- "include": ["**/*.js", "**/*.ts", "**/*.tsx"],
+ "include": ["**/*.ts", "**/*.tsx"],
"exclude": [
// Tests and test infrastructure
"**/test/*.js",
diff --git a/package.json b/package.json
index 4a8fac0ce..224c038c4 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,8 @@
"checkformatting": "prettier --check *.{js,ts,tsx} h/static/scripts",
"format": "prettier --list-different --write *.{js,ts,tsx} h/static/scripts",
"lint": "eslint h/static/scripts",
- "test": "gulp test"
+ "test": "gulp test",
+ "typecheck": "tsc --build h/static/scripts/tsconfig.json"
},
"dependencies": {
"@babel/core": "^7.24.7",
diff --git a/yarn.lock b/yarn.lock
index 3848796d9..7ec5987e2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2705,12 +2705,12 @@ __metadata:
linkType: hard
"@types/vinyl@npm:^2.0.9":
- version: 2.0.11
- resolution: "@types/vinyl@npm:2.0.11"
+ version: 2.0.12
+ resolution: "@types/vinyl@npm:2.0.12"
dependencies:
"@types/expect": ^1.20.4
"@types/node": "*"
- checksum: e921718775992ada7712a26da9635fb970bcdbcf4c4cc832344092d68710ffaf7dd3cce42b0050b2741b2cf75a30f3e1b87e1032ae26552db1faac17094d2b50
+ checksum: 885b7813676eed755b94d42501a56bf36c8d93d8ef6cc4d495bb5779fc5c81ffbcee7d0defe77515a90bf46209968da84b559471611efa0d2900ec75fa1a84de
languageName: node
linkType: hard
The changes in yarn.lock
are the result of removing the existing entry for a package (@types/vinyl
) that was triggering errors and re-running yarn
to add the latest version.
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.
Done: 6c5ccec
tailwind.config.js
Outdated
], | ||
theme: { | ||
extend: { | ||
animation: { |
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.
The fade-in
, fade-out
and slide-in-from-right
animations are now part of our component library so we should be able to remove them here and in Via. I would remove everything related to animations from here in this PR. If we find there is anything missing in the shared library we can add them there. The animations are used by the ToastMessages
component.
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.
Done: 013c74f
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.
No further comments apart from what Rob already mentioned.
This is to make way for adding a new `GroupCreateController` in future.
Add the non-functional beginnings of a new, feature-flagged re-implementation of the create-group page as a Preact app.
Same as we do in Via
The legacy CSS build task doesn't need the Tailwind config.
Same as in client
This should make it easier to turn on type-checking
These are now in our component library so not needed here.
Add our modern frontend stack (TypeScript, Preact, Tailwind, frontend-shared) and add a feature-flagged re-implementation of the create-group form using this modern frontend stack.
To add the new frontend-stack I copied how it has been done in Via, and then just fixed up any issues I ran into.
The new create-group form uses the same HTML and CSS as the legacy form, but is not yet functional.
The new create-group form doesn't use any Tailwind CSS classes or anything from frontend-shared, but these things have been added so it could use them. I'm not sure what direction we'll go in the end: continue to use the legacy CSS classes directly, as in this PR? Re-implement the legacy CSS classes as Tailwind-style classes and frontend-shared components? Break with the legacy style and have the new form look different?
Testing
devdata_admin
, go to http://localhost:5000/admin/features, enable thepreact_create_group_form
feature flag, then go to http://localhost:5000/groups/new. You should see a form that looks almost exactly like the legacy create-group form but isn't functional yet.