Add a bunch of docs
We're making an assumption here that the decorator is actually all over the app when it's not.
This commit is contained in:
parent
0a9985fb48
commit
9865ce899b
3 changed files with 90 additions and 0 deletions
|
@ -67,6 +67,7 @@ practices that anyone working with the SDK needs to be be aware of and uphold:
|
||||||
|
|
||||||
* After creating a new component you must run `yarn reskindex` to regenerate
|
* After creating a new component you must run `yarn reskindex` to regenerate
|
||||||
the `component-index.js` for the SDK (used in future for skinning)
|
the `component-index.js` for the SDK (used in future for skinning)
|
||||||
|
<!-- TODO: Remove this once this approach to skinning is replaced -->
|
||||||
|
|
||||||
* The view's CSS file MUST have the same name (e.g. view/rooms/MessageTile.css).
|
* The view's CSS file MUST have the same name (e.g. view/rooms/MessageTile.css).
|
||||||
CSS for matrix-react-sdk currently resides in
|
CSS for matrix-react-sdk currently resides in
|
||||||
|
|
71
docs/skinning.md
Normal file
71
docs/skinning.md
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
# Skinning
|
||||||
|
|
||||||
|
The react-sdk can be skinned to replace presentation components, CSS, or
|
||||||
|
other relevant parts of the SDK. Typically consumers will replace entire
|
||||||
|
components and get the ability for custom CSS as a result.
|
||||||
|
|
||||||
|
This doc isn't exhaustive on how skinning works, though it should cover
|
||||||
|
some of the more complicated parts such as component replacement.
|
||||||
|
|
||||||
|
## Loading a skin
|
||||||
|
|
||||||
|
1. Generate a `component-index.js` (preferably using the tools that the react-sdk
|
||||||
|
exposes). This can typically be done with a npm script like `"reskindex -h src/header"`.
|
||||||
|
2. In your app's entry point, add something like this code:
|
||||||
|
```javascript
|
||||||
|
import {loadSkin} from "matrix-react-sdk";
|
||||||
|
loadSkin(import("component-index").components);
|
||||||
|
// The rest of your imports go under this.
|
||||||
|
```
|
||||||
|
3. Import the remainder of the SDK and bootstrap your app.
|
||||||
|
|
||||||
|
It is extremely important that you **do not** import anything else from the
|
||||||
|
SDK prior to loading your skin as otherwise the skin might not work. Loading
|
||||||
|
the skin should be one of the first things your app does, if not the very
|
||||||
|
first thing.
|
||||||
|
|
||||||
|
Additionally, **do not** provide `loadSkin` with the react-sdk components
|
||||||
|
themselves otherwise the app might explode. The SDK is already aware of its
|
||||||
|
components and doesn't need to be told.
|
||||||
|
|
||||||
|
## Replacing components
|
||||||
|
|
||||||
|
Components that replace the react-sdk ones MUST have a `replaces` static
|
||||||
|
key on the component's class to describe which component it overrides. For
|
||||||
|
example, if your `VectorAuthPage` component is meant to replace the react-sdk
|
||||||
|
`AuthPage` component then you'd add `static replaces = 'views.auth.AuthPage';`
|
||||||
|
to the `VectorAuthPage` class.
|
||||||
|
|
||||||
|
Other than that, the skin just needs to be loaded normally as mentioned above.
|
||||||
|
Consumers of the SDK likely will not be interested in the rest of this section.
|
||||||
|
|
||||||
|
### SDK developer notes
|
||||||
|
|
||||||
|
Components in the react-sdk MUST be decorated with the `@replaceableComponent`
|
||||||
|
function. For components that can't use the decorator, they must use a
|
||||||
|
variation that provides similar functionality. The decorator gives consumers
|
||||||
|
an opportunity to load skinned components by abusing import ordering and
|
||||||
|
behaviour.
|
||||||
|
|
||||||
|
Decorators are executed at import time which is why we can abuse the import
|
||||||
|
ordering behaviour: importing `loadSkin` doesn't trigger any components to
|
||||||
|
be imported, allowing the consumer to specify a skin. When the consumer does
|
||||||
|
import a component (for example, `MatrixChat`), it starts to pull in all the
|
||||||
|
components via `import` statements. When the components get pulled in the
|
||||||
|
decorator checks with the skinned components to see if it should be replacing
|
||||||
|
the component being imported. The decorator then effectively replaces the
|
||||||
|
components when needed by specifying the skinned component as an override for
|
||||||
|
the SDK's component, which should in theory override critical functions like
|
||||||
|
`render()` and lifecycle event handlers.
|
||||||
|
|
||||||
|
The decorator also means that older usage of `getComponent()` is no longer
|
||||||
|
required because components should be replaced by the decorator. Eventually
|
||||||
|
the react-sdk should only have one usage of `getComponent()`: the decorator.
|
||||||
|
|
||||||
|
The decorator assumes that if `getComponent()` returns null that there is
|
||||||
|
no skinned version of the component and continues on using the SDK's component.
|
||||||
|
In previous versions of the SDK, the function would throw an error instead
|
||||||
|
because it also expected the skin to list the SDK's components as well, however
|
||||||
|
that is no longer possible due to the above.
|
||||||
|
|
||||||
|
In short, components should always be `import`ed.
|
|
@ -17,6 +17,24 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import sdk from '../index';
|
import sdk from '../index';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces a component with a skinned version if a skinned version exists.
|
||||||
|
* This decorator should only be applied to components which can be skinned. For
|
||||||
|
* the react-sdk this means all components should be decorated with this.
|
||||||
|
*
|
||||||
|
* The decoration works by assuming the skin has been loaded prior to the
|
||||||
|
* decorator being called. If that's not the case, the developer will find
|
||||||
|
* out quickly through various amounts of errors and explosions.
|
||||||
|
*
|
||||||
|
* For a bit more detail on how this works, see docs/skinning.md
|
||||||
|
* @param {string} name The dot-path name of the component being replaced.
|
||||||
|
* @param {React.Component} origComponent The component that can be replaced
|
||||||
|
* with a skinned version. If no skinned version is available, this component
|
||||||
|
* will be used.
|
||||||
|
*/
|
||||||
export function replaceableComponent(name: string, origComponent: React.Component) {
|
export function replaceableComponent(name: string, origComponent: React.Component) {
|
||||||
|
// Decorators return a function to override the class (origComponent). This
|
||||||
|
// ultimately assumes that `getComponent()` won't throw an error and instead
|
||||||
|
// return a falsey value like `null` when the skin doesn't have a component.
|
||||||
return () => sdk.getComponent(name) || origComponent;
|
return () => sdk.getComponent(name) || origComponent;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue