Skip to content

Commit

Permalink
Add router to group forms and placeholder page for members view
Browse files Browse the repository at this point in the history
Add a router to the group forms using Wouter to enable client-side transitions,
and use it to enable switching between group settings and group members in the
group edit forms. The "Members" page is currently just a placeholder.
  • Loading branch information
robertknight committed Nov 8, 2024
1 parent fd57a81 commit 539216c
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 32 deletions.
44 changes: 44 additions & 0 deletions h/static/scripts/group-forms/components/AppRoot.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Route, Switch } from 'wouter-preact';
import { useMemo } from 'preact/hooks';

import CreateEditGroupForm from './CreateEditGroupForm';
import EditGroupMembersForm from './EditGroupMembersForm';
import type { ConfigObject } from '../config';
import { Config } from '../config';

export type AppRootProps = {
config: ConfigObject;
};

export default function AppRoot({ config }: AppRootProps) {
const stylesheetLinks = useMemo(
() =>
config.styles.map((stylesheetURL, index) => (
<link
key={`${stylesheetURL}${index}`}
rel="stylesheet"
href={stylesheetURL}
/>
)),
[config],
);

return (
<>
{stylesheetLinks}
<Config.Provider value={config}>
<Switch>
<Route path="/groups/new">
<CreateEditGroupForm />
</Route>
<Route path="/groups/:pubid/edit">
<CreateEditGroupForm />
</Route>
<Route path="/groups/:pubid/edit/members">
<EditGroupMembersForm />
</Route>
</Switch>
</Config.Provider>
</>
);
}
19 changes: 10 additions & 9 deletions h/static/scripts/group-forms/components/CreateEditGroupForm.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { useEffect, useId, useMemo, useState } from 'preact/hooks';
import { useContext, useEffect, useId, useState } from 'preact/hooks';

import {
Button,
CancelIcon,
RadioGroup,
useWarnOnPageUnload,
} from '@hypothesis/frontend-shared';
import { readConfig } from '../config';
import { Config } from '../config';
import { callAPI } from '../utils/api';
import type {
CreateUpdateGroupAPIRequest,
Expand All @@ -15,9 +15,11 @@ import type {
} from '../utils/api';
import { pluralize } from '../utils/pluralize';
import { setLocation } from '../utils/set-location';
import FormContainer from './forms/FormContainer';
import Star from './forms/Star';
import Label from './forms/Label';
import TextField from './forms/TextField';
import GroupFormHeader from './GroupFormHeader';
import SaveStateIcon from './SaveStateIcon';
import WarningDialog from './WarningDialog';

Expand Down Expand Up @@ -84,7 +86,7 @@ function GroupTypeChangeWarning({
}

export default function CreateEditGroupForm() {
const config = useMemo(() => readConfig(), []);
const config = useContext(Config)!;
const group = config.context.group;

const [name, setName] = useState(group?.name ?? '');
Expand Down Expand Up @@ -217,11 +219,10 @@ export default function CreateEditGroupForm() {
};

return (
<div className="text-grey-6 text-sm/relaxed">
<h1 className="mt-14 mb-8 text-grey-7 text-xl/none" data-testid="header">
{heading}
</h1>

<FormContainer title={heading}>
{group && config.features.group_members && (
<GroupFormHeader group={group} />
)}
<form onSubmit={onSubmit} data-testid="form">
<TextField
type="input"
Expand Down Expand Up @@ -327,6 +328,6 @@ export default function CreateEditGroupForm() {
&nbsp;Required
</div>
</footer>
</div>
</FormContainer>
);
}
18 changes: 18 additions & 0 deletions h/static/scripts/group-forms/components/EditGroupMembersForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useContext } from 'preact/hooks';

import { Config } from '../config';
import FormContainer from './forms/FormContainer';
import GroupFormHeader from './GroupFormHeader';

export default function EditGroupMembersForm() {
const config = useContext(Config)!;
const group = config.context.group!;

return (
<FormContainer title="Edit group members">
<GroupFormHeader group={group} />
<hr />
<div className="my-2">Group members will appear here.</div>
</FormContainer>
);
}
42 changes: 42 additions & 0 deletions h/static/scripts/group-forms/components/GroupFormHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import classnames from 'classnames';
import type { ComponentChildren } from 'preact';
import { Link as RouterLink, useRoute } from 'wouter-preact';

import type { Group } from '../config';

type TabLinkProps = {
href: string;
children: ComponentChildren;
};

function TabLink({ children, href }: TabLinkProps) {
const [selected] = useRoute(href);
return (
<RouterLink
href={href}
className={classnames({
'focus-visible-ring whitespace-nowrap flex items-center font-semibold rounded px-2 py-1 gap-x-2':
true,
'text-grey-1 bg-grey-7': selected,
})}
>
{children}
</RouterLink>
);
}

export type GroupFormHeaderProps = {
group: Group;
};

export default function GroupFormHeader({ group }: GroupFormHeaderProps) {
const editLink = `/groups/${group.pubid}/edit`;
const editMembersLinks = `/groups/${group.pubid}/edit/members`;

return (
<div className="flex gap-x-2 justify-end py-2">
<TabLink href={editLink}>Settings</TabLink>
<TabLink href={editMembersLinks}>Members</TabLink>
</div>
);
}
18 changes: 18 additions & 0 deletions h/static/scripts/group-forms/components/forms/FormContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { ComponentChildren } from 'preact';

export type FormContainerProps = {
title: string;
children: ComponentChildren;
};

/** A container for a form with a title. */
export default function FormContainer({ children, title }: FormContainerProps) {
return (
<div className="text-grey-6 text-sm/relaxed">
<h1 className="mt-14 mb-8 text-grey-7 text-xl/none" data-testid="header">
{title}
</h1>
{children}
</div>
);
}
22 changes: 14 additions & 8 deletions h/static/scripts/group-forms/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createContext } from 'preact';
import type { GroupType } from './utils/api';

export type APIConfig = {
Expand All @@ -6,6 +7,15 @@ export type APIConfig = {
headers: Record<PropertyKey, unknown>;
};

export type Group = {
pubid: string;
name: string;
description: string;
link: string;
type: GroupType;
num_annotations: number;
};

export type ConfigObject = {
/** The URLs of the app's CSS stylesheets. */
styles: string[];
Expand All @@ -14,16 +24,10 @@ export type ConfigObject = {
updateGroup: APIConfig | null;
};
context: {
group: {
pubid: string;
name: string;
description: string;
link: string;
type: GroupType;
num_annotations: number;
} | null;
group: Group | null;
};
features: {
group_members: boolean;
group_type: boolean;
};
};
Expand All @@ -32,3 +36,5 @@ export type ConfigObject = {
export function readConfig(): ConfigObject {
return JSON.parse(document.querySelector('.js-config')!.textContent!);
}

export const Config = createContext<ConfigObject | null>(null);
17 changes: 2 additions & 15 deletions h/static/scripts/group-forms/index.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,13 @@
import { render } from 'preact';

import CreateEditGroupForm from './components/CreateEditGroupForm';
import AppRoot from './components/AppRoot';
import { readConfig } from './config';

function init() {
const shadowHost = document.querySelector('#create-group-form')!;
const shadowRoot = shadowHost.attachShadow({ mode: 'open' });
const config = readConfig();
const stylesheetLinks = config.styles.map((stylesheetURL, index) => (
<link
key={`${stylesheetURL}${index}`}
rel="stylesheet"
href={stylesheetURL}
/>
));
render(
<>
{stylesheetLinks}
<CreateEditGroupForm />
</>,
shadowRoot,
);
render(<AppRoot config={config} />, shadowRoot);
}

init();

0 comments on commit 539216c

Please sign in to comment.