Skip to content
This repository was archived by the owner on Jan 30, 2025. It is now read-only.

Commit 7368dbf

Browse files
maheichykweeman1337germain-ggMikhail Aheichyk
authored
Add support for rendering a custom wrapper around Element (#13)
Co-authored-by: Michael Weimann <[email protected]> Co-authored-by: Germain <[email protected]> Co-authored-by: Mikhail Aheichyk <[email protected]>
1 parent 6a605f6 commit 7368dbf

File tree

8 files changed

+700
-9
lines changed

8 files changed

+700
-9
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ being raised for additional handling.
9898
The module can also change what room/user/entity the user is looking at, and join it (if it's a room), with
9999
`navigatePermalink` on a `ModuleApi` instance.
100100

101+
### Wrapper management
102+
From the `RuntimeModule` instance, modules can listen for `WrapperLifecycle.Wrapper` to provide a wrapper react component.
103+
It would wrap the `MatrixChat` component and let any consumer add a header, a footer.
104+
101105
## Contributing / developing
102106

103107
Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for the mechanics of the contribution process.

jest.config.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
22
module.exports = {
33
preset: 'ts-jest',
4-
testEnvironment: 'node',
5-
};
4+
testEnvironment: 'jsdom',
5+
setupFilesAfterEnv: ["<rootDir>/test/setupTests.ts"],
6+
};

package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,20 +35,28 @@
3535
"@babel/preset-env": "^7.16.11",
3636
"@babel/preset-react": "^7.16.7",
3737
"@babel/preset-typescript": "^7.16.7",
38+
"@testing-library/jest-dom": "^5.16.5",
39+
"@testing-library/react": "^12.1.5",
3840
"@types/jest": "^27.4.1",
3941
"@types/react": "^17",
42+
"@types/react-dom": "^17.0.19",
4043
"@typescript-eslint/eslint-plugin": "^5.18.0",
4144
"@typescript-eslint/parser": "^5.18.0",
4245
"eslint": "^8.12.0",
4346
"eslint-config-google": "^0.14.0",
4447
"eslint-plugin-import": "^2.26.0",
4548
"eslint-plugin-matrix-org": "^0.4.0",
4649
"jest": "^27.5.1",
50+
"react": "17.0.2",
51+
"react-dom": "^17.0.2",
4752
"rimraf": "^3.0.2",
4853
"ts-jest": "^27.1.4",
4954
"typescript": "^4.6.3"
5055
},
5156
"dependencies": {
5257
"@babel/runtime": "^7.17.9"
58+
},
59+
"peerDependencies": {
60+
"react": "^17.0.2"
5361
}
5462
}

src/lifecycles/WrapperLifecycle.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
Copyright 2023 Mikhail Aheichyk
3+
Copyright 2023 Nordeck IT + Consulting GmbH.
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
import { ComponentType, PropsWithChildren } from 'react';
19+
20+
/**
21+
* Wrapper lifecycle events
22+
*/
23+
export enum WrapperLifecycle {
24+
/**
25+
* An event to request the wrapper. It is sent by Element to get the wrapper provided by the module if any.
26+
*/
27+
Wrapper = 'wrapper'
28+
}
29+
30+
/**
31+
* Opts object that is populated with a Wrapper.
32+
*/
33+
export type WrapperOpts = {
34+
/**
35+
* A Wrapper React Component to be rendered around the Matrix Chat.
36+
*/
37+
Wrapper: ComponentType<PropsWithChildren<{}>>;
38+
};
39+
40+
/**
41+
* Helper type that documents how to implement a wrapper listener.
42+
*/
43+
export type WrapperListener = (opts: WrapperOpts) => void;

src/lifecycles/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ limitations under the License.
1616

1717
import { RoomViewLifecycle } from "./RoomViewLifecycle";
1818
import { WidgetLifecycle } from "./WidgetLifecycle";
19+
import { WrapperLifecycle } from "./WrapperLifecycle";
1920

2021
export type AnyLifecycle =
2122
| RoomViewLifecycle
2223
| WidgetLifecycle
24+
| WrapperLifecycle
2325
;
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
Copyright 2023 Mikhail Aheichyk
3+
Copyright 2023 Nordeck IT + Consulting GmbH.
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
import React from 'react';
19+
import { render, screen } from '@testing-library/react';
20+
21+
import { RuntimeModule } from '../../src/RuntimeModule';
22+
import { WrapperLifecycle, WrapperListener, WrapperOpts } from '../../src/lifecycles/WrapperLifecycle';
23+
24+
describe('WrapperLifecycle', () => {
25+
let module: RuntimeModule;
26+
27+
beforeAll(() => {
28+
module = new class extends RuntimeModule {
29+
constructor() {
30+
super(undefined as any);
31+
32+
this.on(WrapperLifecycle.Wrapper, this.wrapperListener);
33+
}
34+
35+
protected wrapperListener: WrapperListener = (wrapperOpts: WrapperOpts) => {
36+
wrapperOpts.Wrapper = ({ children }) => {
37+
return <>
38+
<header>Header</header>
39+
{children}
40+
<footer>Footer</footer>
41+
</>;
42+
};
43+
};
44+
};
45+
});
46+
47+
it('should wrap a matrix client with header and footer', () => {
48+
const opts: WrapperOpts = { Wrapper: React.Fragment };
49+
module.emit(WrapperLifecycle.Wrapper, opts);
50+
51+
render(<opts.Wrapper><span>MatrixChat</span></opts.Wrapper>);
52+
53+
const header = screen.getByRole('banner');
54+
expect(header).toBeInTheDocument();
55+
const matrixChat = screen.getByText(/MatrixChat/i);
56+
expect(matrixChat).toBeInTheDocument();
57+
const footer = screen.getByRole('contentinfo');
58+
expect(footer).toBeInTheDocument();
59+
60+
expect(header.nextSibling).toBe(matrixChat);
61+
expect(matrixChat.nextSibling).toBe(footer);
62+
});
63+
});
64+

test/setupTests.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
Copyright 2023 Mikhail Aheichyk
3+
Copyright 2023 Nordeck IT + Consulting GmbH.
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
import "@testing-library/jest-dom";

0 commit comments

Comments
 (0)