Skip to content

Commit

Permalink
Add dynamic routing
Browse files Browse the repository at this point in the history
Pull data dynamically from API to allow for interaction with canned assistants.
  • Loading branch information
rebeccaalpert committed Oct 16, 2024
1 parent 3c5f03b commit e3a877e
Show file tree
Hide file tree
Showing 16 changed files with 232 additions and 316 deletions.
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.

59 changes: 44 additions & 15 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,55 @@ import {
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();

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

Check warning on line 60 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,9 +124,7 @@ const AppLayout: React.FunctionComponent<IAppLayout> = ({ children }) => {

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}>
{route.label}
</NavLink>
<NavLink to={route.path}>{route.label}</NavLink>
</NavItem>
);

Expand All @@ -111,11 +142,9 @@ const AppLayout: React.FunctionComponent<IAppLayout> = ({ children }) => {
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 +178,7 @@ const AppLayout: React.FunctionComponent<IAppLayout> = ({ children }) => {
skipToContent={PageSkipToContent}
isContentFilled
>
{children}
<Outlet />
</Page>
);
};
Expand Down
Loading

0 comments on commit e3a877e

Please sign in to comment.