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

HMS-1623 feat: add detail page #20

Merged
merged 15 commits into from
Nov 9, 2023
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
19 changes: 8 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,7 @@ In order to access the https://[env].foo.redhat.com in your browser, you have to

To setup the hosts file run following command:

```bash
npm run patch:hosts
```

If this command throws an error run it as a `sudo`:

```bash
sudo npm run patch:hosts
```

The commands adds these entries to `/etc/hosts`
Add the below to your `/etc/hosts` file:

```
127.0.0.1 prod.foo.redhat.com
Expand All @@ -25,6 +15,13 @@ The commands adds these entries to `/etc/hosts`
127.0.0.1 ci.foo.redhat.com
```

## Install react developer tools

A recommended tool to install is react developer tools, which is installed as a plugin for your
favourite browser.

- [React Developer Tools](https://react.dev/learn/react-developer-tools).

## Setup and run chrome-service-backend

Clone repositories and use Alejandro's branch:
Expand Down
85 changes: 49 additions & 36 deletions docs/04-context.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ creation of a context for the micro-frontend.
towards the ListDomains.
- Provide the same data information for the different impacted objects.

> Avoid to use context if it evoke a performance because an excess of the
> render on the frontend.

## Defining the context

For using a context, we need to define the context (in this case it is defined
Expand All @@ -17,78 +20,88 @@ it were required).
import { createContext } from 'react';
import { Domain } from './Api';

export interface IAppContext {
export interface AppContextType {
domains: Domain[];
setDomains: (domains: Domain[]) => void;
}

export const AppContext = createContext<IAppContext>({
domains: [],
setDomains: (domains: Domain[]) => {
throw new Error('Function "setDomains" not implemented: domains=' + domains);
},
});
export const AppContext = createContext<AppContextType>(undefined);

export const AppContextProvider: React.FC<AppContextProviderProps> = ({ children }) => {
const [getter, setter] = useState<T>(undefined);

return (
<AppContext.Provider
value={{
/** Map the interface to the getter and setter of this component for instance:
* domains: getter,
* setDomains: setter,
*/
},
}}
>
{children}
</AppContext.Provider>
);
}
```

Let's see what we have here:

- IAppContext define an interface with properties to be shared and some
- `AppContextType` define an interface with properties to be shared and some
callbacks to update the properties above.
- The callbacks will use some state associated to a root component, so
when we change the state from a deeper component, the view is properly
rendered.

We will define the callbacks at `src/AppEntry.tsx`:
- Create the context for the type `AppContextType`.
- Define the `AppContextProvider` which map getter and setter, and extend with
the `children`.

## Referencing the context in the root component
## Wrap your application by the context provider

```typescript
import React, { useState } from 'react';
import React from 'react';

// ...

const AppEntry = () => {
// Declare the state
const [domains, setDomains] = useState<Domain[]>([]);
const cbSetDomains = (domains: Domain[]) => {
setDomains(domains);
};
return (
<Provider store={init(...(process.env.NODE_ENV !== 'production' ? [logger] : [])).getStore()}>
<Router basename={getBaseName(window.location.pathname)}>
<AppContext.Provider value={{ domains: domains, setDomains: cbSetDomains }}>
<AppContextProvider>
<App />
</AppContext.Provider>
</AppContextProvider>
</Router>
</Provider>
);
};
```

As we see the callback is invoking the setter for the state, which raise the
render mechanism for the view.

See that we inject the domains value and the setter callback into the `value` property
for the `AppContext.Provider` tag.
Now we only reference the `AppContextProvider` wrapping our application. The
setter and getter are configured inside the AppContextProvider, which provide
a cleaner code.

## Use the context form other inner components

Using the context from any component nested in our App would be kind of the below:

```typescript
const appContext = useContext(AppContext);
// we get the reference to the context
const appContext = useContext<AppContextType | undefined>(AppContext);

const domains = appContext.domains; // Retrieving the value from the context
// We use a property
const domains = appContext?.domains;

// We use a setter which will fire a render cycle with the change
const myEvent = () => {
appContext.setDomains(newDomain); // This will fire from the root component the render
appContext?.setDomains(newDomains);
};
```

What is happening here?
- I get the reference to the context.
- I read a value from the context.
- I set a value into the context by using the injected callback when we
used the context into the root component; recall that it is calling a setDomain state,
which is the one that trigger the render of the view.
> Remember we mapped the set... function returned by useState hook
> for this callback, so we are calling a change of the state for
> the context. So it is calling a setDomain state, which is the one
> that trigger the render of the view.

## Final considerations

Similar approach could be followed when we want to define a context closer to some component,
such as the wizard page (not using this approach currently), but instead of wrapping the application
we would wrap an inner component.
145 changes: 114 additions & 31 deletions src/AppContext.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,44 @@
import { createContext } from 'react';
import { ReactNode, createContext, useState } from 'react';
import { Domain } from './Api';
import { VerifyState } from './Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry';
import React from 'react';

/**
* It represents the application context so common events and properties
* are shared for many components, making their values accessible.
* @public
*/
export interface IAppContext {
export interface AppContextType {
/** Represent the current list of domains to be displayed in the listDomains view. */
domains: Domain[];
/** Callback to set the value of `domains`. */
setDomains: (domains: Domain[]) => void;
/** Update an existing domain in domains */
updateDomain: (domain: Domain) => void;
/** Delete the domain identified by id */
deleteDomain: (id: string) => void;
/** Get the domain identified by id */
getDomain: (id: string) => Domain | undefined;
/** The current editing domain */
editing?: Domain;
/** Set the current editing domain */
setEditing: (value?: Domain) => void;

/** Encapsulates the context related with the wizard. */
wizard: {
/** Retrieve the current token, required to register a domain. */
getToken: () => string;
token: string;
/** Set the value of the token. */
setToken: (value: string) => void;

/** Retrieve the value of the registered status which is updated once
* the user has registered the domain by using ipa-hcc tool. */
getRegisteredStatus: () => VerifyState;
registeredStatus: VerifyState;
/** Setter for the registered status. */
setRegisteredStatus: (value: VerifyState) => void;

/** Get the ephemeral domain state that manage the wizard. */
getDomain: () => Domain;
domain: Domain;
/** Set the ephemeral domain information. */
setDomain: (value: Domain) => void;
};
Expand All @@ -34,29 +48,98 @@ export interface IAppContext {
* Represent the application context.
* @public
*/
export const AppContext = createContext<IAppContext>({
domains: [],
setDomains: (domains: Domain[]) => {
throw new Error('Function "setDomains" not implemented: domains=' + domains);
},
wizard: {
getToken: (): string => {
return '';
},
setToken: (value: string) => {
throw new Error('Function "setToken" not implemented: value=' + value);
},
getRegisteredStatus: (): VerifyState => {
return 'initial';
},
setRegisteredStatus: (value: VerifyState) => {
throw new Error('Function "setRegisteredStatus" not implemented: value=' + value);
},
getDomain: (): Domain => {
return {} as Domain;
},
setDomain: (value: Domain) => {
throw new Error('Function "setDomain" not implemented: value=' + value);
},
},
});
export const AppContext = createContext<AppContextType | undefined>(undefined);

/**
* The properties accepted by the AppContextProvider.
*/
interface AppContextProviderProps {
/** The children components. */
children: ReactNode;
}

/**
* Define the provider for the application context.
* @param param0 The children components.
* @returns the application context.
*/
export const AppContextProvider: React.FC<AppContextProviderProps> = ({ children }) => {
const [domains, _setDomains] = useState<Domain[]>([]);
const [editing, _setEditing] = useState<Domain>();

const [wizardToken, _setWizardSetToken] = useState<string>();
const [wizardRegisteredStatus, _setWizardRegisteredStatus] = useState<VerifyState>('initial');
const [wizardDomain, _setWizardDomain] = useState<Domain>();

/**
* Update a domain into the list of domains kept into the application context
* if it exists.
* @param domain The domain to be updated into the context.
*/
const _updateDomain = (domain: Domain) => {
const newDomains: Domain[] = {} as Domain[];
for (const idx in domains) {
if (domains[idx].domain_id === domain.domain_id) {
newDomains[idx] = domain;
} else {
newDomains[idx] = domains[idx];
}
}
_setDomains(newDomains);
};

/**
* Delete a domain from the application context if it exists, which is
* identified by the its id.
* @param id the domain identifier.
*/
const _deleteDomain = (id: string) => {
const newDomains: Domain[] = {} as Domain[];
for (const idx in domains) {
if (domains[idx].domain_id !== id) {
newDomains[idx] = domains[idx];
}
}
_setDomains(newDomains);
};

/**
* Retrieve a domain from the application context if it exists.
* @param id the domain identifier.
* @returns The domain that exists into the application context
* or undefined if it is not found.
*/
const _getDomain = (id: string): Domain | undefined => {
if (id === '') return undefined;
for (const idx in domains) {
if (domains[idx].domain_id === id) {
return domains[idx];
}
}
return undefined;
};

return (
<AppContext.Provider
value={{
domains: domains,
setDomains: _setDomains,
updateDomain: _updateDomain,
deleteDomain: _deleteDomain,
getDomain: _getDomain,
editing: editing,
setEditing: _setEditing,
wizard: {
token: wizardToken || '',
setToken: _setWizardSetToken,
registeredStatus: wizardRegisteredStatus,
setRegisteredStatus: _setWizardRegisteredStatus,
domain: wizardDomain || ({} as Domain),
setDomain: _setWizardDomain,
},
}}
>
{children}
</AppContext.Provider>
);
};
Loading
Loading