Skip to content

Commit

Permalink
(hds-2141) Improve stories and documentation
Browse files Browse the repository at this point in the history
Some tabs in the login stories did not update then data was renew.

Also the docs did not indicate strongly enough that LoginProvider context does not re-render everytime data is changed.
  • Loading branch information
NikoHelle committed May 6, 2024
1 parent db5cb9e commit 7e1dd45
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 18 deletions.
77 changes: 62 additions & 15 deletions packages/react/src/components/login/Login.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ import {
LoginProviderProps,
} from './index';
import { Button } from '../button/Button';
import { Accordion } from '../accordion/Accordion';
import { Header } from '../header/Header';
import { Notification } from '../notification/Notification';
import { IconSignout, IconUser } from '../../icons';
import { Tabs } from '../tabs/Tabs';
import { Logo, logoFi } from '../logo';
import { LoadingSpinner } from '../loadingSpinner';

type StoryArgs = {
useKeycloak?: boolean;
Expand All @@ -66,7 +66,7 @@ const useKeycloakArgs = {
description: 'Only a storybook option. If true, Keycloak OIDC is used.',
};

// To use this in localhost, copy the settings from https://hds.hel.fi/components/login/ and change
// To use this in localhost, copy the settings from https://hds.hel.fi/components/login/#common-settings-for-localhost and change
// with Tunnistamo
// redirect_uri: `${window.origin}/static-login/callback.html`
// or with Keycloak:
Expand All @@ -75,6 +75,12 @@ const useKeycloakArgs = {
// silent_redirect_uri: `${window.origin}/static-login/silent_renew.html`
// post_logout_redirect_uri: `${window.origin}/static-login/logout.html`

// For hds-demo -site, use the localhost settings. The demo url must be https://city-of-helsinki.github.io/hds-demo/login
// because that is registered to the OIDC provider.
// All "_uri"s must be ${window.origin}/hds-demo/login/static-login/{xxx}.html
// Also make this change to html files in the storybookStatic -folder:
// const prefix = `${window.origin}/hds-demo/login/`;

const loginProviderProps: LoginProviderProps = {
userManagerSettings: {
authority: 'https://tunnistamo.test.hel.ninja/',
Expand Down Expand Up @@ -316,11 +322,8 @@ const SimulateSessionEndButton = () => {
return <Button onClick={onButtonClick}>Simulate session end</Button>;
};

const UserData = ({ user }: { user: User }) => {
const ProfileData = ({ user }: { user: User }) => {
const profile = user.profile as Profile;
const expiresAt = new Date();
expiresAt.setTime(user.expires_at ? user.expires_at * 1000 : Date.now());
const timezoneOffset = expiresAt.getTimezoneOffset();
return (
<div>
<p>
Expand All @@ -335,11 +338,6 @@ const UserData = ({ user }: { user: User }) => {
<p>
Your level of assurance is <strong>&quot;{profile.loa}&quot;</strong>.
</p>
<p>
Your tokens will expire{' '}
{new Intl.DateTimeFormat('en-FI', { dateStyle: 'full', timeStyle: 'long', timeZone: 'GMT' }).format(expiresAt)}
{timezoneOffset !== 0 ? `+ ${timezoneOffset / -60} hour(s)` : ''}
</p>
</div>
);
};
Expand Down Expand Up @@ -400,6 +398,22 @@ const UserProfileOutput = ({ user }: { user: User }) => {
const ApiTokenOutput = () => {
const { getStoredApiTokens } = useApiTokens();
const [, tokens] = getStoredApiTokens();
const listener = useCallback(() => true, []);
useSignalListener(listener);

const apiTokensClient = useApiTokensClient();
const oidcClient = useOidcClient();

const isRenewing = apiTokensClient.isRenewing() || oidcClient.isRenewing();
if (isRenewing) {
return (
<>
<LoadingSpinner small />
<span>Renewing data...</span>
</>
);
}

return (
<div>
<p>This are your api tokens:</p>
Expand Down Expand Up @@ -446,6 +460,41 @@ const TrackAllSignals = () => {
);
};

const DynamicUserData = () => {
const apiTokensClient = useApiTokensClient();
const oidcClient = useOidcClient();
const listener = useCallback(() => true, []);
useSignalListener(listener);

const isRenewing = apiTokensClient.isRenewing() || oidcClient.isRenewing();
if (isRenewing) {
return (
<>
<LoadingSpinner small />
<span>Renewing data...</span>
</>
);
}

const user = oidcClient.getUser() as User;
if (!user) {
return null;
}
const expiresAt = new Date();
expiresAt.setTime(user.expires_at ? user.expires_at * 1000 : Date.now());
const timezoneOffset = expiresAt.getTimezoneOffset();
return (
<div>
<p>
Your tokens will expire{' '}
{new Intl.DateTimeFormat('en-FI', { dateStyle: 'full', timeStyle: 'long', timeZone: 'GMT' }).format(expiresAt)}
{timezoneOffset !== 0 ? `+ ${timezoneOffset / -60} hour(s)` : ''}
</p>
<UserProfileOutput user={user} />
</div>
);
};

const AuthorizedContent = ({ user }: { user: User }) => {
return (
<Tabs>
Expand All @@ -456,10 +505,8 @@ const AuthorizedContent = ({ user }: { user: User }) => {
<Tabs.Tab>Show errors</Tabs.Tab>
</Tabs.TabList>
<Tabs.TabPanel>
<UserData user={user} />
<Accordion heading="Show full user info">
<UserProfileOutput user={user} />
</Accordion>
<ProfileData user={user} />
<DynamicUserData />
</Tabs.TabPanel>
<Tabs.TabPanel>
<ApiTokenOutput />
Expand Down
3 changes: 1 addition & 2 deletions site/src/docs/components/login/api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ export const CustomisationPageAnchorLink = ({ anchor, children }) => {

- <AnchorLink>Beacon</AnchorLink>
- <AnchorLink>Signals</AnchorLink>
- <AnchorLink anchor="important-1">Important</AnchorLink>

### Components

Expand Down Expand Up @@ -668,7 +667,7 @@ Only custom modules can emit signals. See the <CustomisationPageAnchorLink ancho

See also <CustomisationPageAnchorLink anchor="custom-namespaced-beacons-for-modules">custom namespaced beacons</CustomisationPageAnchorLink>.

#### Important
##### Important

If a listener is added while signaling and it is triggered by the currently emitted signal, a loop could be created. This could occur when using hooks and not memoizing listeners. Avoid emitting signals inside listeners.

Expand Down
8 changes: 7 additions & 1 deletion site/src/docs/components/login/usage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,13 @@ All component properties are listed on the <ApiPageAnchorLink anchor="logincallb
#### LoginProvider

This component creates a React context and initialises all modules. All components and hooks are required to render inside the LoginProvider or React Context will throw an error.
Position the context in the component tree like any other React context. The context does not change and cause re-renders often, so it can be at the top level.
Position the context in the component tree like any other React context.

The value of context does not change every time one of its modules changes. Therefore, it does not cause re-renders, so it can be at the top level. For example, if user data or API tokens are renewed, the value of the context is not updated. Only its modules. This way, the application using the LoginProvider is not constantly re-rendered.

Up-to-date user data or API tokens can be accessed from the modules with hooks.

The <AnchorLink>useSignalListener</AnchorLink> can be used to update a component when a module of the LoginProvider changes.

<PlaygroundPreview>

Expand Down

0 comments on commit 7e1dd45

Please sign in to comment.