Skip to content

Commit

Permalink
feat(Settings): Add Settings Page
Browse files Browse the repository at this point in the history
In the scope of selecting the Runtime in use, a settings page pointing
to a catalog URL is needed.

This commit adds a Settings component and a Settings provider. By
default, it will store the settings to the browser's LocalStorage.

The idea behind using a SettingsAdapter is to give the possibilit in the
future to set the settings at VSCode level if needed.

fix: #834
  • Loading branch information
lordrip committed Jun 28, 2024
1 parent be908dc commit f4bf6d0
Show file tree
Hide file tree
Showing 40 changed files with 1,087 additions and 73 deletions.
51 changes: 34 additions & 17 deletions packages/ui/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,52 @@
import { Outlet } from 'react-router-dom';
import { useReload } from './hooks/reload.hook';
import { Shell } from './layout/Shell';
import { LocalStorageSettingsAdapter } from './models/settings/localstorage-settings-adapter';
import {
CatalogLoaderProvider,
CatalogTilesProvider,
EntitiesProvider,
RuntimeProvider,
SchemasLoaderProvider,
SettingsProvider,
SourceCodeProvider,
VisibleFlowsProvider,
} from './providers';
import { isDefined } from './utils';
import { CatalogSchemaLoader } from './utils/catalog-schema-loader';

function App() {
const ReloadProvider = useReload();
const settingsAdapter = new LocalStorageSettingsAdapter();
let catalogUrl = CatalogSchemaLoader.DEFAULT_CATALOG_PATH;
const settingsCatalogUrl = settingsAdapter.getSettings().catalogUrl;

if (isDefined(settingsCatalogUrl) && settingsCatalogUrl !== '') {
catalogUrl = settingsCatalogUrl;
}

return (
<SourceCodeProvider>
<EntitiesProvider>
<Shell>
<RuntimeProvider catalogUrl={CatalogSchemaLoader.DEFAULT_CATALOG_PATH}>
<SchemasLoaderProvider>
<CatalogLoaderProvider>
<CatalogTilesProvider>
<VisibleFlowsProvider>
<Outlet />
</VisibleFlowsProvider>
</CatalogTilesProvider>
</CatalogLoaderProvider>
</SchemasLoaderProvider>
</RuntimeProvider>
</Shell>
</EntitiesProvider>
</SourceCodeProvider>
<ReloadProvider>
<SettingsProvider adapter={settingsAdapter}>
<SourceCodeProvider>
<EntitiesProvider>
<Shell>
<RuntimeProvider catalogUrl={catalogUrl}>
<SchemasLoaderProvider>
<CatalogLoaderProvider>
<CatalogTilesProvider>
<VisibleFlowsProvider>
<Outlet />
</VisibleFlowsProvider>
</CatalogTilesProvider>
</CatalogLoaderProvider>
</SchemasLoaderProvider>
</RuntimeProvider>
</Shell>
</EntitiesProvider>
</SourceCodeProvider>
</SettingsProvider>
</ReloadProvider>
);
}

Expand Down
15 changes: 15 additions & 0 deletions packages/ui/src/assets/settingsSchema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"additionalProperties": false,
"description": "JSON Schema for Kaoto configuration",
"properties": {
"catalogUrl": {
"title": "Camel Catalog URL",
"description": "URL that points to the `index.json` file from the Kaoto Camel Catalog. Leave it empty to use the embedded catalog.",
"default": "<empty string>",
"type": "string",
"format": "uri"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { render } from '@testing-library/react';
import { AbstractSettingsAdapter, DefaultSettingsAdapter } from '../../models/settings';
import { ReloadContext, SettingsProvider } from '../../providers';
import { LoadDefaultCatalog } from './LoadDefaultCatalog';

describe('LoadDefaultCatalog', () => {
let reloadPage: jest.Mock;
let settingsAdapter: AbstractSettingsAdapter;

beforeEach(() => {
reloadPage = jest.fn();
settingsAdapter = new DefaultSettingsAdapter();
});

it('should render correctly', () => {
const wrapper = render(
<ReloadContext.Provider value={{ reloadPage, lastRender: 0 }}>
<SettingsProvider adapter={settingsAdapter}>
<LoadDefaultCatalog errorMessage="Test error message" />
</SettingsProvider>
</ReloadContext.Provider>,
);

expect(wrapper).toMatchSnapshot();
});

it('should render child components', () => {
const wrapper = render(
<ReloadContext.Provider value={{ reloadPage, lastRender: 0 }}>
<SettingsProvider adapter={settingsAdapter}>
<LoadDefaultCatalog errorMessage="Test error message">
<div>Test children</div>
</LoadDefaultCatalog>
</SettingsProvider>
</ReloadContext.Provider>,
);

const child = wrapper.getByText('Test children');

expect(child).toBeInTheDocument();
});

it('should call reloadCatalog on button click', () => {
const wrapper = render(
<ReloadContext.Provider value={{ reloadPage, lastRender: 0 }}>
<SettingsProvider adapter={settingsAdapter}>
<LoadDefaultCatalog errorMessage="Test error message">
<div>Test children</div>
</LoadDefaultCatalog>
</SettingsProvider>
</ReloadContext.Provider>,
);

const button = wrapper.getByText('Reload with default Catalog');
button.click();

expect(reloadPage).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {
Bullseye,
Button,
EmptyState,
EmptyStateActions,
EmptyStateBody,
EmptyStateFooter,
EmptyStateHeader,
EmptyStateIcon,
EmptyStateVariant,
ExpandableSection,
} from '@patternfly/react-core';
import { ExclamationCircleIcon } from '@patternfly/react-icons';
import { FunctionComponent, PropsWithChildren, useContext } from 'react';
import { useReloadContext } from '../../hooks/useReloadContext/useReloadContext';
import { SettingsContext } from '../../providers';

interface ILoadDefaultCatalogProps {
errorMessage: string;
}

export const LoadDefaultCatalog: FunctionComponent<PropsWithChildren<ILoadDefaultCatalogProps>> = (props) => {
const settingsAdapter = useContext(SettingsContext);
const { reloadPage } = useReloadContext();

const reloadCatalog = () => {
const currentSettings = settingsAdapter.getSettings();
settingsAdapter.saveSettings({ ...currentSettings, catalogUrl: '' });
reloadPage();
};

return (
<Bullseye>
<EmptyState variant={EmptyStateVariant.lg} data-testid="load-default-catalog">
<EmptyStateHeader
titleText="The Catalog couldn't be loaded"
headingLevel="h4"
icon={<EmptyStateIcon icon={ExclamationCircleIcon} />}
/>

<EmptyStateBody>{props.children}</EmptyStateBody>

<EmptyStateFooter>
<EmptyStateActions>
<Button variant="primary" onClick={reloadCatalog}>
Reload with default Catalog
</Button>
</EmptyStateActions>
<ExpandableSection
toggleText="Error details"
toggleId="expandable-section-toggle"
contentId="expandable-section-content"
>
<code>
<pre>{JSON.stringify(props.errorMessage, null, 2)}</pre>
</code>
</ExpandableSection>
</EmptyStateFooter>
</EmptyState>
</Bullseye>
);
};
Loading

0 comments on commit f4bf6d0

Please sign in to comment.