Skip to content
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
5 changes: 0 additions & 5 deletions src/Main.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,4 @@ describe('Main', () => {
const { getByTestId } = renderWrapper(<Main />);
expect(getByTestId('authz-module')).toBeInTheDocument();
});

it('wraps the body in an xl container to align it with the header', () => {
const { getByTestId } = renderWrapper(<Main />);
expect(getByTestId('authz-module').closest('.container-mw-xl')).toBeInTheDocument();
});
});
5 changes: 1 addition & 4 deletions src/Main.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { CurrentAppProvider, PageWrap, getSiteConfig, useIntl } from '@openedx/frontend-base';
import { Container } from '@openedx/paragon';
import { Helmet } from 'react-helmet';

import { appId } from './constants';
Expand All @@ -20,9 +19,7 @@ const Main = () => {
</title>
</Helmet>
<PageWrap>
<Container size="xl" fluid className="px-0">
<AuthZModule />
</Container>
<AuthZModule />
</PageWrap>
</CurrentAppProvider>
);
Expand Down
18 changes: 5 additions & 13 deletions src/authz-module/audit-user/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ import {
useContext, useEffect, useMemo, useState,
} from 'react';
import { SiteContext, useIntl } from '@openedx/frontend-base';
import {
Container, DataTable,
} from '@openedx/paragon';
import { DataTable } from '@openedx/paragon';
import TableFooter from '@src/authz-module/components/TableFooter/TableFooter';
import {
AUTHZ_HOME_PATH, TABLE_DEFAULT_PAGE_SIZE,
Expand Down Expand Up @@ -228,7 +226,7 @@ const AuditUserPage = () => {
};

return (
<div className="authz-module">
<>
<ConfirmDeletionModal
isOpen={showConfirmDeletionModal}
close={handleCloseConfirmDeletionModal}
Expand All @@ -243,11 +241,6 @@ const AuditUserPage = () => {
}}
/>
<AuthZLayout
context={{
id: '',
org: '',
title: '',
}}
navLinks={navLinks}
activeLabel={user?.username || ''}
pageTitle={user?.username || ''}
Expand All @@ -258,7 +251,7 @@ const AuditUserPage = () => {
]
}
>
<Container className="bg-light-200 p-5">
<div className="page-band py-5">
<DataTable
isPaginated
isFilterable
Expand All @@ -283,10 +276,9 @@ const AuditUserPage = () => {
<DataTable.Table />
<TableFooter />
</DataTable>

</Container>
</div>
</AuthZLayout>
</div>
</>
);
};

Expand Down
40 changes: 18 additions & 22 deletions src/authz-module/authz-home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,25 @@ const AuthzHome = () => {
const pageTitle = intl.formatMessage(messages['authz.manage.page.title']);

return (
<div className="authz-module">
<AuthZLayout
context={{ id: '', title: '', org: '' }}
pageTitle={pageTitle}
pageSubtitle=""
actions={
[<AddRoleButton key="add-role-button" />]
}
<AuthZLayout
pageTitle={pageTitle}
actions={
[<AddRoleButton key="add-role-button" />]
}
>
<Tabs
variant="tabs"
defaultActiveKey={hash ? 'permissionsRoles' : 'team'}
className="page-band bg-light-100"
>
<Tabs
variant="tabs"
defaultActiveKey={hash ? 'permissionsRoles' : 'team'}
className="bg-light-100 px-5"
>
<Tab eventKey="team" title={intl.formatMessage(messages['authz.tabs.team'])} className="p-5 bg-light-200">
<TeamMembersTable presetScope={presetScope} />
</Tab>
<Tab id="libraries-permissions-roles-tab" eventKey="permissionsRoles" title={intl.formatMessage(messages['authz.tabs.permissionsRoles'])}>
<RolesPermissions />
</Tab>
</Tabs>
</AuthZLayout>
</div>
<Tab eventKey="team" title={intl.formatMessage(messages['authz.tabs.team'])} className="page-band py-5">
<TeamMembersTable presetScope={presetScope} />
</Tab>
<Tab id="libraries-permissions-roles-tab" eventKey="permissionsRoles" title={intl.formatMessage(messages['authz.tabs.permissionsRoles'])} className="page-band py-5">
<RolesPermissions />
</Tab>
</Tabs>
</AuthZLayout>
);
};

Expand Down
24 changes: 13 additions & 11 deletions src/authz-module/components/AuthZLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@ import AuthZTitle, { AuthZTitleProps } from './AuthZTitle';

interface AuthZLayoutProps extends AuthZTitleProps {
children: ReactNode;
context: {
id: string;
org: string;
title: string;
};
}

const AuthZLayout = ({ children, ...props }: AuthZLayoutProps) => (
<>
<AuthZTitle {...props} />
{children}
</>

/**
* Page chrome for the authz module: the title band, then the body below it.
* The body only paints its colour, edge to edge. Each page marks the parts of its
* content that should line up with the header using `page-band`, so anything meant
* to bleed across the full width -- a stepper header, a toolbar -- simply omits it.
*/
const AuthZLayout = ({ children, ...titleProps }: AuthZLayoutProps) => (
<div className="authz-module">
<AuthZTitle {...titleProps} />
<div className="bg-light-200">
{children}
</div>
</div>
);

export default AuthZLayout;
2 changes: 0 additions & 2 deletions src/authz-module/components/AuthZTitle.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@ describe('AuthZTitle', () => {
const defaultProps = {
activeLabel: 'Current Page',
pageTitle: 'Page Title',
pageSubtitle: 'Page Subtitle',
};

it('renders without optional fields', () => {
render(<AuthZTitle {...defaultProps} />);
expect(screen.getByText(defaultProps.activeLabel)).toBeInTheDocument();
expect(screen.getByText(defaultProps.pageTitle)).toBeInTheDocument();
expect(screen.getByText(defaultProps.pageSubtitle as string)).toBeInTheDocument();
});

it('renders breadcrumb with links and active label', () => {
Expand Down
21 changes: 13 additions & 8 deletions src/authz-module/components/AuthZTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
} from 'react';
import { Link } from 'react-router-dom';
import {
Breadcrumb, Col, Container, Row, Button,
Breadcrumb, Col, Row, Button,
Stack,
useMediaQuery,
breakpoints,
Expand All @@ -23,7 +23,7 @@ interface Action {
export interface AuthZTitleProps {
activeLabel?: string;
pageTitle: string;
pageSubtitle: string | ReactNode;
pageSubtitle?: string | ReactNode;
navLinks?: BreadcrumbLink[];
actions?: (Action | ReactNode)[];
}
Expand All @@ -42,7 +42,7 @@ const AuthZTitle = ({
}: AuthZTitleProps) => {
const isDesktop = useMediaQuery({ minWidth: breakpoints.large.minWidth });
return (
<Container className="p-5 bg-light-100">
<div className="page-band py-5 bg-light-100">
<Breadcrumb
linkAs={Link}
links={navLinks}
Expand All @@ -52,9 +52,14 @@ const AuthZTitle = ({
<Col xs={12} md={7} className="mb-4">
<div className="d-flex align-items-center flex-column-sm">
<h2 className="text-primary mb-0">{pageTitle}</h2>
{typeof pageSubtitle === 'string'
? <> { pageSubtitle !== '' && <hr className="mx-lg-3" /> }<h3 className="mb-0 py-2 font-weight-light text-gray-700">{pageSubtitle}</h3></>
: <>{ pageSubtitle !== '' && <hr className="mx-lg-3" /> } <div className="mb-0">{pageSubtitle}</div></>}
{pageSubtitle && (
<>
<hr className="authz-action-divider mx-lg-3" />
{typeof pageSubtitle === 'string'
? <h3 className="mb-0 py-2 font-weight-light text-gray-700">{pageSubtitle}</h3>
: <div className="mb-0">{pageSubtitle}</div>}
</>
)}

</div>
</Col>
Expand All @@ -72,15 +77,15 @@ const AuthZTitle = ({
<Fragment key={`authz-header-action-${key}`}>
{content}
{(index === actions.length - 1) ? null
: (<hr className="mx-lg-5" />)}
: (<hr className="authz-action-divider mx-lg-5" />)}
</Fragment>
);
})
}
</Stack>
</Col>
</Row>
</Container>
</div>
);
};

Expand Down
5 changes: 4 additions & 1 deletion src/authz-module/components/PermissionTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ interface PermissionTableProps {
const PermissionTable = ({ permissionsTable, roles, title }: PermissionTableProps) => {
const { formatMessage } = useIntl();
return (
<Card>
// Only the narrowest viewports are too small for the matrix, so the card scrolls there
// (see `.permission-table-scroll`). Above that it is not a scroll container, which is
// what lets the header row stick to the viewport as the page scrolls.
<Card className="permission-table-scroll">
<table className="permission-table w-100">
<thead>
<tr>
Expand Down
22 changes: 13 additions & 9 deletions src/authz-module/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,12 @@
min-height: 700px;
}

hr {
.authz-action-divider {
border-right: var(--pgn-size-border-width) solid var(--pgn-color-border);
height: var(--height-action-divider);
width: 0;
}

.tab-content {
background-color: var(--pgn-color-light-200);
}

.collapsible-card {
border: none;

Expand Down Expand Up @@ -66,12 +62,23 @@
line-height: 24px;
}
}

// Paragon's `.overflow-auto` lives in the `paragon` cascade layer, where its `!important`
// outranks anything this layer can say, so the card carries its own class instead. The
// matrix is 432px at min-content: only the narrowest screens need the card to scroll, and
// the cap is what gives the sticky header something to stick to while it is the scroller.
@media (--pgn-size-breakpoint-max-width-xs) {
.permission-table-scroll {
overflow: auto;
max-height: 80vh;
}
}

@media(--pgn-size-breakpoint-max-width-sm){
.flex-column-sm {
flex-direction: column;
}
hr {
.authz-action-divider {
border-top: var(--pgn-size-border-width) solid var(--pgn-color-border);
border-right: none;
width: 100%;
Expand Down Expand Up @@ -126,6 +133,3 @@
max-height: 500px;
}

.scope-search-input {
width: 18.75rem; // 300px
}
4 changes: 2 additions & 2 deletions src/authz-module/role-assignation-wizard/AssignRoleWizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ const AssignRoleWizard = ({
<Stepper activeKey={activeStep}>
<Stepper.Header className="bg-info-100" />

<div className="bg-light-200 p-5">
<div className="page-band py-5">
<Stepper.Step
eventKey={STEPS.SELECT_USERS_AND_ROLE}
title={intl.formatMessage(messages['wizard.step.selectUsersAndRole.title'])}
Expand Down Expand Up @@ -191,7 +191,7 @@ const AssignRoleWizard = ({
</Stepper.Step>
</div>

<div className="p-5">
<div className="page-band py-5">
<Stepper.ActionRow eventKey={STEPS.SELECT_USERS_AND_ROLE}>
<Button variant="outline-primary" onClick={handleClose}>
{intl.formatMessage(messages['wizard.button.cancel'])}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,9 @@ const AssignRoleWizardPage = () => {

return (
<AuthZLayout
context={{ id: '', title: '', org: '' }}
navLinks={[{ label: intl.formatMessage(messages['wizard.page.breadcrumb']), to: returnTo }]}
activeLabel={intl.formatMessage(messages['wizard.page.title'])}
pageTitle={intl.formatMessage(messages['wizard.page.title'])}
pageSubtitle=""
actions={[]}
>
<AssignRoleWizard
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,16 @@ const ScopeFilterBar = ({
return (
<>
<div className="d-flex align-items-center justify-content-between gap-3 mb-2 flex-wrap">
<div className="d-flex align-items-center gap-3">
<div className="scope-search-input">
<Form.Group controlId="scope-search" className="mb-0">
<Form.Control
type="text"
value={search}
onChange={(e: ChangeEvent<HTMLInputElement>) => onSearchChange(e.target.value)}
placeholder={intl.formatMessage(messages['wizard.step2.search.placeholder'])}
trailingElement={<Icon src={Search} />}
/>
</Form.Group>
</div>
<div className="d-flex align-items-center gap-3 flex-wrap">
<Form.Group controlId="scope-search" className="mb-0">
<Form.Control
type="text"
value={search}
onChange={(e: ChangeEvent<HTMLInputElement>) => onSearchChange(e.target.value)}
placeholder={intl.formatMessage(messages['wizard.step2.search.placeholder'])}
trailingElement={<Icon src={Search} />}
/>
</Form.Group>

<OrgFilter
filterButtonText={intl.formatMessage(messages['wizard.step2.filter.org.label'])}
Expand Down
8 changes: 4 additions & 4 deletions src/authz-module/roles-permissions/RolesPermissions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const RolesPermissions = () => {
}, [intl]);

return (
<Container className="p-5">
<>
<Container className="pb-5">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the outer Container gone, this one is centred with Paragon's breakpoint max-widths, so the toggle drifts up to ~100px right of the table at tablet widths. A plain <div className="pb-5"> fixes it.

<ButtonGroup size="lg" className="mb-2">
<Button
Expand Down Expand Up @@ -81,14 +81,14 @@ const RolesPermissions = () => {
className="mt-5"
>
<div className="row align-items-center">
<div className="col col-7">
<div className="col-12 col-md-7">
<p className="text-primary font-weight-bold h4">{intl.formatMessage(messages['authz.tabs.permissionsRoles.courses.alert.title'])}</p>
<span>
<span className="font-weight-bold">{intl.formatMessage(messages['authz.tabs.permissionsRoles.courses.alert.note'])}</span>
{intl.formatMessage(messages['authz.tabs.permissionsRoles.courses.alert.description'])}
</span>
</div>
<div className="col col-5">
<div className="col-12 col-md-5 mt-3 mt-md-0">
<Hyperlink className="d-block text-right h5 font-weight-normal" destination="https://docs.openedx.org/en/latest/educators/references/course_development/course_team_roles.html" target="_blank" showLaunchIcon={false} isInline>
{intl.formatMessage(messages['authz.tabs.permissionsRoles.courses.alert.link'])}
</Hyperlink>
Expand All @@ -106,7 +106,7 @@ const RolesPermissions = () => {
/>
)}
<AnchorButton />
</Container>
</>
);
};

Expand Down
Loading
Loading