Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
37 changes: 7 additions & 30 deletions web/packages/teleterm/src/ui/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,50 +17,27 @@
import React from 'react';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import styled from 'styled-components';

import { Failed } from 'design/CardError';

import { AppInitializer } from 'teleterm/ui/AppInitializer';

import CatchError from './components/CatchError';
import { CatchError } from './components/CatchError';
import { StyledApp } from './components/App';
import AppContextProvider from './appContextProvider';
import AppContext from './appContext';
import { StaticThemeProvider, ThemeProvider } from './ThemeProvider';
import { darkTheme } from './ThemeProvider/theme';
import { ThemeProvider } from './ThemeProvider';

export const App: React.FC<{ ctx: AppContext }> = ({ ctx }) => {
return (
<StyledApp>
<CatchError>
<CatchError>
<StyledApp>
<DndProvider backend={HTML5Backend}>
<AppContextProvider value={ctx}>
<ThemeProvider>
<AppInitializer />
</ThemeProvider>
</AppContextProvider>
</DndProvider>
</CatchError>
</StyledApp>
);
};

export const FailedApp = (props: { message: string }) => {
return (
<StyledApp>
<StaticThemeProvider theme={darkTheme}>
<Failed alignSelf={'baseline'} message={props.message} />
</StaticThemeProvider>
</StyledApp>
</StyledApp>
</CatchError>
);
};

const StyledApp = styled.div`
left: 0;
right: 0;
top: 0;
bottom: 0;
position: absolute;
display: flex;
flex-direction: column;
`;
3 changes: 2 additions & 1 deletion web/packages/teleterm/src/ui/boot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import ReactDOM from 'react-dom';
import React from 'react';

import { ElectronGlobals } from 'teleterm/types';
import { App, FailedApp } from 'teleterm/ui/App';
import { App } from 'teleterm/ui/App';
import { FailedApp } from 'teleterm/ui/components/App';
import AppContext from 'teleterm/ui/appContext';
import Logger from 'teleterm/logger';

Expand Down
29 changes: 29 additions & 0 deletions web/packages/teleterm/src/ui/components/App.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Teleport
* Copyright (C) 2024 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { screen, render } from '@testing-library/react';
import '@testing-library/jest-dom';

import { FailedApp } from './App';

describe('<FailedApp>', () => {
it('renders (without ThemeProvider being available)', () => {
render(<FailedApp message="Lorem ipsum" />);
expect(screen.getByText('Lorem ipsum')).toBeInTheDocument();
});
});
46 changes: 46 additions & 0 deletions web/packages/teleterm/src/ui/components/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Teleport
* Copyright (C) 2024 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import styled from 'styled-components';
import { Failed } from 'design/CardError';

import { StaticThemeProvider } from 'teleterm/ui/ThemeProvider';
import { darkTheme } from 'teleterm/ui/ThemeProvider/theme';

export const StyledApp = styled.div`
left: 0;
right: 0;
top: 0;
bottom: 0;
position: absolute;
display: flex;
flex-direction: column;
`;

export const FailedApp = (props: { message: string }) => {
return (
<StyledApp>
{/*
FailedApp is used above ThemeProvider in the component hierarchy. Since it cannot depend on
ThemeProvider to provide a theme, it needs to use StaticThemeProvider to provide one.
*/}
<StaticThemeProvider theme={darkTheme}>
<Failed alignSelf={'baseline'} message={props.message} />
</StaticThemeProvider>
</StyledApp>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ limitations under the License.
*/

import React from 'react';
import { Failed } from 'design/CardError';

import { FailedApp } from 'teleterm/ui/components/App';
import Logger from 'teleterm/logger';

export default class CatchError extends React.Component {
export class CatchError extends React.Component {
logger = new Logger('components/CatchError');

static getDerivedStateFromError(error) {
Expand All @@ -37,7 +37,7 @@ export default class CatchError extends React.Component {
render() {
if (this.state.error) {
return (
<Failed alignSelf={'baseline'} message={this.state.error.message} />
<FailedApp message={this.state.error?.message || this.state.error} />
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* Teleport
* Copyright (C) 2024 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { screen, render } from '@testing-library/react';
import '@testing-library/jest-dom';

import Logger, { NullService } from 'teleterm/logger';

import { CatchError } from './CatchError';

beforeAll(() => {
Logger.init(new NullService());
});

beforeEach(() => {
jest.restoreAllMocks();
// Mock console.error, otherwise the test would output noise to the terminal.
jest.spyOn(console, 'error').mockImplementation(() => {});
});

const ThrowError = (props: { error: any }) => {
throw props.error;
};

it('renders caught error (without ThemeProvider being available)', () => {
render(
<CatchError>
<ThrowError error={new Error('Lorem ipsum')} />
</CatchError>
);
expect(screen.getByText('Lorem ipsum')).toBeInTheDocument();
expect(console.error).toHaveBeenCalled();
});

it('works correctly when a non-Error value is thrown', () => {
render(
<CatchError>
<ThrowError error={'Lorem ipsum'} />
</CatchError>
);
expect(screen.getByText('Lorem ipsum')).toBeInTheDocument();
expect(console.error).toHaveBeenCalled();
});
3 changes: 1 addition & 2 deletions web/packages/teleterm/src/ui/components/CatchError/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,4 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import CatchError from './CatchError';
export default CatchError;
export { CatchError } from './CatchError';