- Removed custom form reducers in favour of redux-form (updateField(), addForm(), etc)
- Storing 'state' in schema reducer to avoid a separate forms reducer tree (no longer needed for other use cases). This means 'state' is only the "initial state", any form edits will be handled by redux-form internally in the 'reduxForm' reducer namespace
- Removed componentWillUnmount() from <Form> since there's no more reducer state to clean up (removed formReducer.js), and redux-form handles that internally
- Removed isFetching state from <FormBuilder> since there's now a props.submitting coming from redux-form
- Improved passing of "clicked button" (submittingAction), using component state rather than reducer and passing it into action handlers (no need for components to inspect it any other way)
- Hacky duplication of this.submittingAction and this.state.submittingAction to avoid re-render of <FormBuilder> *during* a submission (see https://github.com/erikras/redux-form/issues/1944)
- Inlined handleSubmit() XHR (rather than using a redux action). There's other ways for form consumers to listen to form evens (e.g. onSubmitSuccess passed through <FormBuilder> into reduxForm()).
- Adapting checkbox/radio fields to redux-forms
Need to send onChange event with values rather than the original event,
see http://redux-form.com/6.1.1/docs/api/Field.md/#-input-onchange-eventorvalue-function-
- Using reduxForm() within render() causes DOM to get thrown away,
and has weird side effects like https://github.com/erikras/redux-form/issues/1944.
See https://github.com/erikras/redux-form/issues/603#issuecomment-176397728
- Refactored <FormBuilderLoader> as a separate container component which connects to redux and redux-form. This keeps the <FormBuilder> component dependency free and easy to test. It'll also be an advantage if we switch to a GraphQL backed component, in which case the async loading routines will look very different from the current <FormBuilderLoader> implementation
- Refactoring out the redux-form dependency from FormBuilder to make it more testable (through baseFormComponent and baseFieldComponent)
- Passing through 'form' to allow custom identifiers (which is important because currently the schema "id" contains the record identifier, making the form identifier non-deterministic - which means you can't use it with the redux-form selector API)
The URL to request the schema representation is the unique identifier.
We can't default to $request->getURL() since that's different for form submissions.
The schema.schema_url key is redundant, since the identifier is already contained on the top level 'id' key.
Keeping schema_url in a schema itself makes it less portable, particularly once we transition into
generic schemas which are not reliant on a particular record context (that's only contained in the schema_url)
This also fixes the issue of form schemas not refreshing after submit,
e.g. when form fields are added or removed.
NEW: Add SilverStripe\ORM\UnexpectedDataException class.
This change provides more graceful handling of the case where a
ChangeSetItem is referencing a no-longer-existing class.
The new exception, SilverStripe\ORM\UnexpectedDataException, is
intended to be available for throwing whenever we have unexpected data
in the database.
It can be trapped by the relevant UIs and more graceful errors than
HTTP 500s can be provided.
The props.tag default was set directly on props,
but the component used it from props.data.tag,
hence the default was ineffective.
Since empty arrays for props.data override nested React defaults,
there's no built-in way to enforce this default.
Hardcoding a default felt viable here, since the component allows overrides via props.
We can't use our own "id" values here, since react-bootstrap requires internal consistency
between tab containers and tab panes through its own auto-generated identifiers
This doesn’t have much impact on resulting JS size, but it will
hopefully make merge conflicts much less frequent.
The CSS growth is a little higher (6.5% increase in size) but is not
material.
If this materially reduces the number of merge conflicts we have, by
letting the git merge tools resolve some dist file mergers, I think it
would be worth it.
Some example changes in file size:
bundle.js 290K -> 301K
vendor.js 1,325K -> 1,321K
bundle.css 628K -> 669K
* Rename bundles (prep for webpack optimisation)
This might or might not reduce the overall repo size,
because git can combine similar chunks in the newly generated files
* Optimise webpack build time
Consolidates bundles, since a separation of bundle-framework.js vs. bundle-legacy.js
vs. bundle-lib.js no longer makes sense - they're all loaded upfront anyway,
since we'll be introducing more react-powered logic alongside the "legacy" JavaScript.
By consolidating into fewer bundles, we give the optimisation scripts (UglifyJS)
more options to reduce the overall file size.
The main motivation for a vendor.js is to shorten rebuild times:
Most active development is happening in files required through bundle.js.
This commit drastically reduces the rebuild time for those changes (15s to 4s).
* Panel/tab-panel and alerts spacing, button padding consistency and alignment
* Reports panel spacing adjustments
* ReportAdmin panel and toolbar spacing
* Comment change
* Fix formatting help toggle link
* Use standard line-heights and padding for buttons
* Add base panel styles
* Update to .panel styles and .toolbar spacing
* Remove legacy styles, linting fixes
* Toolbar--content to have consistent styles throughout
* Add panel and toolbar styles to areas missing them
* Replace values with variables
* Layout overrides for tabs and panels with padding
* Adjust JQueryUI button spacing to match other UI buttons
* Remove custom ReportAdmin styles
Update values to variables and modify panel and tab-panel spacing
* Remove text color override
* Remove double (.m-t-1) spacing from campaign panel
* Profile page remove legacy JLayout
* Remove legacy spacing
* Removed Layout from page so !important not needed
* Improve use of variables
* Add missing closing bracket, minor linting fixes
* Linting fixes
* Remove css importants
* Add temp fix for file upload within gridfield
Tidy structure of css
* css build
* Spacing bug fixed for campaign list alert
* Added Created field to File/Image editor
* switch default input value to null
Fix react errors and added a field description
* API Use new DBField::getSchemaValue()
The ‘npm run lint’ command will be used to run listing on Travis, which
can also be used on local dev environments. These can also be used with
editor plugins to highlight errors immediately.
The intention is that this can be used in place of codeclimate. The
benefits are that we use a single toolchain in both CI and local dev,
which is not entirely the case at the moment.
Note the sass-lint is provided by “sudo gem install scss_lint”. It’s
possible that we can move to a node-based sass-lint; I can’t recall
what the motivation for using the scss_lint gem was - I think it was
mainly that we had the AirBNB styleguide already implemented as a linter
config.
Code was assuming that FRAMEWORK_DIR would always have a value. If it
doesn’t (because you’re running a test instance of a naked framework)
This caused URLs to be treated as root relative, which creates some
duplicate-inclusion bugs.
The use of ltrim() works, but is a bit clumsy. A more flexible approach
to including front-end assets of given modules would be preferable;
ideally something that also let you keep your code outside of the
web root.
When rewriting the i18n.js file from ES5 to ES6, the detectLocale()
call in the constructor was missed - meaning the lang files were loaded by the browser,
but never actually used.
The 'admin' module will be split off from 'framework',
where 'framework' only provides (mostly) frontend-agnostic PHP classes.
For example, HTMLEditorField.php has a TinyMCEConfig.php driver,
but doesn't come with its own JS includes.
Multiple entry points can't result in a single bundle.css with a fixed filename, see
https://github.com/webpack/extract-text-webpack-plugin/issues/179
Until that's resolved, it's easier to keep the 'css' task separate in Webpack,
and have a single entry point for all CSS (bundle.scss).
Also partially reverting "Moved frontend assets into admin/ "module"",
which moved too many files: debug.css and install.css need to remain
as framework (not admin) deps. Split out into a separate `framework-css` Webpack
task in preparation for splitting off the module.
The JavaScript i18n functionality in SilverStripe is used in the CMS as well as form field implementations.
Form fields used to include their own JavaScript for usage outside of CMS. This now requires custom build tooling in a project.
Hence there's no need for an i18n shim (i18nx.js), since the CMS always uses i18n support.
We've removed the ability to directly reference JS and CSS files
for form fields and other SilverStripe features in favour of a common bundle built by Webpack.
The logical next step is to make the framework module free of frontend dependencies,
which should simplify its operation, and avoid another time intensive "npm install" on a module.
Used in iframe, so can't be rolled into other code.
Moved in admin/ since that's easier for now with Webpack entry points,
and we'll move all JS/CSS into admin/ anyway soon.
There's not a lot of benefit in packaging these separately in terms of initial CMS load size,
so let's simplify the setup. They'll eventually become lazy loaded chunks in a React-based setup
When adding the deps straight into the file (recommended),
the onchange handler in file-upload isn't firing properly when a file is uploaded through
the <input type=file> button - it falls back to default behaviour, which submits the
containing form, failing because the upload is handled by a different URL.
These were originally copied from node_modules via an "npm run thirdparty" task,
in order to have them loadable with oldschool <script> tags.
Since webpack supports CommonJS-style loading, that's no longer required,
we can simply inline those scripts into the bundle.
We need to use imports-loader though, in order to ensure
that "define" is not available in some module scopes,
which triggers AMD behavior that's not compatible with Webpack's loaders.
See http://webpack.github.io/docs/shimming-modules.html
I've had to pin to the exact versions used in the 3.x CMS,
since jquery-upload has introduced an AMD wrapper sometime
between 6.0 and 6.9 (the latest version NPM automatically pulls in).
This AMD wrapper confuses Webpack, since it's trying to resolve the
dependencies contained in it. We could create shims for those instead,
but the easiest way was to simply revert to the versions already used
before the Webpack migration (since the newer versions in node_modules
were never actually copied into thirdparty, they weren't used before).
Many of these are now inline via Webpack's url-loader plugin (https://github.com/webpack/url-loader),
and a tonne were legacy references (e.g. leftovers from ComplexTableField).
Some of these might still be used in other modules like userforms,
but we're removing support for "hotlinking" images in core - they're not an API.
Responsibility for finding and referencing images and fonts is now
given to webpack. All the url references are now relative to the
component scss file, and point to font & images files in src/, rather
than assuming someone else will place them in dist.
This makes the source more modular, and makes it easier to, for
example, inline images are data URIs, or create a new build script that
builds several modules for a project in a single pass.
Workaround for bad font path in bundle.css:
ExtactTextPlugin didn’t work as well with a subfolder reference in the
filename. This is just a short-term fix and could probably be improved
to put bundle.css back in the styles subfolder.
Webpack handles images & fonts:
Responsibility for finding and referencing images and fonts is now
given to webpack. All the url references are now relative to the
component scss file, and point to font & images files in src/, rather
than assuming someone else will place them in dist.
This makes the source more modular, and makes it easier to, for
example, inline images are data URIs, or create a new build script that
builds several modules for a project in a single pass.
Clarify docs on spriting and webfonts:
We've decided to remove sprity since it comes with hundreds of dependencies,
and needs compilation within the "npm install" - dragging out the already overweight
install process, and making the resulting node_modules/ folder less portable between systems.
The bundle is generated by running “webpack” directly - gulp is no
longer needed as an intermediary. The resulting config is a lot shorter,
although more configuration is pushed into lib.js.
Modules are shared between javascript files as global variables.
Although this global state pollution is a bit messy, I don’t think it’s
practically any worse than the previous state, and it highlights the
heavy coupling between the different packages we have in place.
Reducing the width of the coupling between the core javascript and
add-on modules would probably be a better way of dealing with this than
replacing global variables with some other kind of global state.
The web pack execution seems roughly twice as fast - if I clear out my
framework/client/dist/js folder, it takes 13.3s to rebuild. However,
it’s not rebuilding other files inside dist, only the bundle files.
CSS files are now included from javascript and incorporated into
bundle.css by the webpack. Although the style-loader is helpful in some
dev workflows (it allows live reload), it introduces a flash of
unstyled content which makes it inappropriate for production.
Instead ExtractTextPlugin is used to write all the aggregated CSS
into a single bundle.css file. A style-loader-based configuration could
be introduced for dev environments, if we make use of the webpack live
reloader in the future.
Note that the following features have been removed as they don't appear to be
necessary when using Webpack:
- UMD module generation
- thirdparty dist file copying
LeftAndMain.js deps: Without it, ssui.core.js gets loaded too late,
which leads e.g. to buttons being initialised without this added behaviour.
When you want to add a button to the CMS but don't want LeftAndMain to apply jQuery UI button to it we add the `data-button="true"` attribute to our button.
The `onadd` function checks that this attribute does not exist before calling `this.button()`.
The `onremove` incorrectly checks that this attribute does exist before calling `this.button('destroy')`. This should be checking that this attribute does not exist, just like the `onadd` function.
What this causes is when you have a button with the `data-button` attribute a button is not created, but when you leave the page `destory` gets called on an item which doesn't exist. We end up with the following error:
> Uncaught Error: cannot call methods on button prior to initialization; attempted to call method 'destroy'
The other issue with this logic is buttons are never getting destroyed when `onremove` is called.
The whole issue is caused by a missing `!` in the if statement. This change adds it in to fix the problem.