Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dynamic routing #1

Merged
merged 3 commits into from
Oct 16, 2024
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
38 changes: 25 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ git clone https://github.com/patternfly/patternfly-react-seed
cd patternfly-react-seed
npm install && npm run start:dev
```

## Development scripts

```sh
# Install development/build dependencies
npm install
Expand Down Expand Up @@ -44,54 +46,61 @@ npm run start
```

## Configurations
* [TypeScript Config](./tsconfig.json)
* [Webpack Config](./webpack.common.js)
* [Jest Config](./jest.config.js)
* [Editor Config](./.editorconfig)

- [TypeScript Config](./tsconfig.json)
- [Webpack Config](./webpack.common.js)
- [Jest Config](./jest.config.js)
- [Editor Config](./.editorconfig)

## Raster image support

To use an image asset that's shipped with PatternFly core, you'll prefix the paths with "@assets". `@assets` is an alias for the PatternFly assets directory in node_modules.

For example:

```js
import imgSrc from '@assets/images/g_sizing.png';
<img src={imgSrc} alt="Some image" />
<img src={imgSrc} alt="Some image" />;
```

You can use a similar technique to import assets from your local app, just prefix the paths with "@app". `@app` is an alias for the main src/app directory.

```js
import loader from '@app/assets/images/loader.gif';
<img src={loader} alt="Content loading" />
<img src={loader} alt="Content loading" />;
```

## Vector image support

Inlining SVG in the app's markup is also possible.

```js
import logo from '@app/assets/images/logo.svg';
<span dangerouslySetInnerHTML={{__html: logo}} />
<span dangerouslySetInnerHTML={{ __html: logo }} />;
```

You can also use SVG when applying background images with CSS. To do this, your SVG's must live under a `bgimages` directory (this directory name is configurable in [webpack.common.js](./webpack.common.js#L5)). This is necessary because you may need to use SVG's in several other context (inline images, fonts, icons, etc.) and so we need to be able to differentiate between these usages so the appropriate loader is invoked.

```css
body {
background: url(./assets/bgimages/img_avatar.svg);
}
```

## Adding custom CSS

When importing CSS from a third-party package for the first time, you may encounter the error `Module parse failed: Unexpected token... You may need an appropriate loader to handle this file typ...`. You need to register the path to the stylesheet directory in [stylePaths.js](./stylePaths.js). We specify these explicitly for performance reasons to avoid webpack needing to crawl through the entire node_modules directory when parsing CSS modules.

## Code quality tools
* For accessibility compliance, we use [react-axe](https://github.com/dequelabs/react-axe)
* To keep our bundle size in check, we use [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer)
* To keep our code formatting in check, we use [prettier](https://github.com/prettier/prettier)
* To keep our code logic and test coverage in check, we use [jest](https://github.com/facebook/jest)
* To ensure code styles remain consistent, we use [eslint](https://eslint.org/)

- For accessibility compliance, we use [react-axe](https://github.com/dequelabs/react-axe)
- To keep our bundle size in check, we use [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer)
- To keep our code formatting in check, we use [prettier](https://github.com/prettier/prettier)
- To keep our code logic and test coverage in check, we use [jest](https://github.com/facebook/jest)
- To ensure code styles remain consistent, we use [eslint](https://eslint.org/)

## Multi environment configuration

This project uses [dotenv-webpack](https://www.npmjs.com/package/dotenv-webpack) for exposing environment variables to your code. Either export them at the system level like `export MY_ENV_VAR=http://dev.myendpoint.com && npm run start:dev` or simply drop a `.env` file in the root that contains your key-value pairs like below:

```sh
Expand All @@ -109,4 +118,7 @@ The assistants will need to be dynamic. Eventually we are going to want the abil

Add a button to stop a streaming response

(Infra) Set the Registry Secrets at the org level
(Infra) Set the Registry Secrets at the org level

I will say (and we can sync about this if it does not make sense), we will need to be able to dynamically specify the backend's base url, since this is going to be a demo deployed on openshift where that URL will be auto generated.
My optimal case would be something where we could build an image that would read an Environment variable that has the backend url on startup and that way we could just deploy that image in different environments no problem. But it looks like that may not be possible, seems like that variable needs to be defined at build time.
103 changes: 31 additions & 72 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"raw-loader": "^4.0.2",
"react-axe": "^3.5.4",
"react-docgen-typescript-loader": "^3.7.2",
"react-router-dom": "^5.3.4",
"react-router-dom": "^6.27.0",
"regenerator-runtime": "^0.13.11",
"rimraf": "^5.0.7",
"style-loader": "^3.3.4",
Expand Down
12 changes: 0 additions & 12 deletions src/app/Ansible/Ansible.tsx

This file was deleted.

63 changes: 50 additions & 13 deletions src/app/AppLayout/AppLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { NavLink, useLocation } from 'react-router-dom';
import { NavLink, Outlet, useLoaderData, useLocation } from 'react-router-dom';
import {
Button,
Masthead,
Expand All @@ -9,22 +9,61 @@
MastheadToggle,
Nav,
NavExpandable,
NavGroup,
NavItem,
NavList,
Page,
PageSidebar,
PageSidebarBody,
SkipToContent,
} from '@patternfly/react-core';
import { IAppRoute, IAppRouteGroup, routes } from '@app/routes';
import { IAppRoute, IAppRouteGroup, routes as staticRoutes } from '@app/routes';
import { BarsIcon } from '@patternfly/react-icons';
interface IAppLayout {
children: React.ReactNode;
import { CannedChatbot } from '../types/CannedChatbot';

const getChatbots = () => {
const url = process.env.REACT_APP_INFO_URL ?? '';
return fetch(url)
.then((res) => res.json())
.then((data: CannedChatbot[]) => {
return data;
})
.catch((e) => {
throw new Response(e.message, { status: 404 });
});
};

export async function loader() {
const chatbots = await getChatbots();
return { chatbots };
}

const AppLayout: React.FunctionComponent<IAppLayout> = ({ children }) => {
const AppLayout: React.FunctionComponent = () => {
const [sidebarOpen, setSidebarOpen] = React.useState(true);
const [routes, setRoutes] = React.useState(staticRoutes);
const { chatbots } = useLoaderData() as { chatbots: CannedChatbot[] };

React.useEffect(() => {
if (chatbots) {
const newRoutes = structuredClone(routes);
chatbots.forEach((chatbot) => {
const isNotPresent =
routes.filter((route) => {
if ('path' in route) {
return route.path === `assistants/${chatbot.name}`;
}
return false;
}).length === 0;
if (isNotPresent) {
newRoutes.push({
path: `assistants/${chatbot.name}`,
label: chatbot.displayName,
title: chatbot.displayName,
});
}
});
setRoutes(newRoutes);
}
}, []);

Check warning on line 66 in src/app/AppLayout/AppLayout.tsx

View workflow job for this annotation

GitHub Actions / Lint

React Hook React.useEffect has missing dependencies: 'chatbots' and 'routes'. Either include them or remove the dependency array

const masthead = (
<Masthead>
Expand Down Expand Up @@ -91,7 +130,7 @@

const renderNavItem = (route: IAppRoute, index: number) => (
<NavItem key={`${route.label}-${index}`} id={`${route.label}-${index}`} isActive={route.path === location.pathname}>
<NavLink exact={route.exact} to={route.path}>
<NavLink to={route.path} reloadDocument>
{route.label}
</NavLink>
</NavItem>
Expand All @@ -111,11 +150,9 @@
const Navigation = (
<Nav id="nav-primary-simple">
<NavList id="nav-list-simple">
<NavGroup title="AI assistants">
{routes.map(
(route, idx) => route.label && (!route.routes ? renderNavItem(route, idx) : renderNavGroup(route, idx)),
)}
</NavGroup>
{routes.map(
(route, idx) => route.label && (!route.routes ? renderNavItem(route, idx) : renderNavGroup(route, idx)),
)}
</NavList>
</Nav>
);
Expand Down Expand Up @@ -149,7 +186,7 @@
skipToContent={PageSkipToContent}
isContentFilled
>
{children}
<Outlet />
</Page>
);
};
Expand Down
Loading
Loading