feat: create layaut module components
This commit is contained in:
committed by
Adolfo R. Brandes
parent
80d04ed000
commit
b825a8bdd9
27
src/authz-module/components/AuthZLayout.tsx
Normal file
27
src/authz-module/components/AuthZLayout.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { StudioHeader } from '@edx/frontend-component-header';
|
||||
import AuthZTitle, { AuthZTitleProps } from './AuthZTitle';
|
||||
|
||||
interface AuthZLayoutProps extends AuthZTitleProps {
|
||||
children: ReactNode;
|
||||
context: {
|
||||
id: string;
|
||||
org: string;
|
||||
title: string;
|
||||
}
|
||||
}
|
||||
|
||||
const AuthZLayout = ({ children, context, ...props }: AuthZLayoutProps) => (
|
||||
<>
|
||||
<StudioHeader
|
||||
number={context.id}
|
||||
org={context.org}
|
||||
title={context.title}
|
||||
/>
|
||||
<AuthZTitle {...props} />
|
||||
{children}
|
||||
</>
|
||||
|
||||
);
|
||||
|
||||
export default AuthZLayout;
|
||||
62
src/authz-module/components/AuthZTitle.test.tsx
Normal file
62
src/authz-module/components/AuthZTitle.test.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import React from 'react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import AuthZTitle, { AuthZTitleProps } from './AuthZTitle';
|
||||
|
||||
describe('AuthZTitle', () => {
|
||||
const defaultProps: AuthZTitleProps = {
|
||||
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', () => {
|
||||
const navLinks = [
|
||||
{ label: 'Root', to: '/' },
|
||||
{ label: 'Section', to: '/section' },
|
||||
];
|
||||
|
||||
render(<AuthZTitle {...defaultProps} navLinks={navLinks} />);
|
||||
|
||||
navLinks.forEach(({ label }) => {
|
||||
expect(screen.getByText(label)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(screen.getByText(defaultProps.activeLabel)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders page title', () => {
|
||||
render(<AuthZTitle {...defaultProps} />);
|
||||
expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent(defaultProps.pageTitle);
|
||||
});
|
||||
|
||||
it('renders page subtitle as ReactNode', () => {
|
||||
const subtitleNode = <div data-testid="custom-subtitle">Custom Subtitle</div>;
|
||||
render(<AuthZTitle {...defaultProps} pageSubtitle={subtitleNode} />);
|
||||
expect(screen.getByTestId('custom-subtitle')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders action buttons and triggers onClick', () => {
|
||||
const onClick1 = jest.fn();
|
||||
const onClick2 = jest.fn();
|
||||
const actions = [
|
||||
{ label: 'Save', onClick: onClick1 },
|
||||
{ label: 'Cancel', onClick: onClick2 },
|
||||
];
|
||||
|
||||
render(<AuthZTitle {...defaultProps} actions={actions} />);
|
||||
|
||||
actions.forEach(({ label, onClick }) => {
|
||||
const button = screen.getByRole('button', { name: label });
|
||||
expect(button).toBeInTheDocument();
|
||||
fireEvent.click(button);
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
50
src/authz-module/components/AuthZTitle.tsx
Normal file
50
src/authz-module/components/AuthZTitle.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { ReactNode } from 'react';
|
||||
import {
|
||||
Breadcrumb, Col, Container, Row, Button, Badge,
|
||||
} from '@openedx/paragon';
|
||||
|
||||
interface BreadcrumbLink {
|
||||
label: string;
|
||||
to?: string;
|
||||
}
|
||||
|
||||
interface Action {
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export interface AuthZTitleProps {
|
||||
activeLabel: string;
|
||||
pageTitle: string;
|
||||
pageSubtitle: string | ReactNode;
|
||||
navLinks?: BreadcrumbLink[];
|
||||
actions?: Action[];
|
||||
}
|
||||
|
||||
const AuthZTitle = ({
|
||||
activeLabel, navLinks = [], pageTitle, pageSubtitle, actions = [],
|
||||
}: AuthZTitleProps) => (
|
||||
<Container className="p-5 bg-light-100">
|
||||
<Breadcrumb
|
||||
links={navLinks}
|
||||
activeLabel={activeLabel}
|
||||
/>
|
||||
<Row className="mt-4">
|
||||
<Col xs={12} md={8} className="mb-4">
|
||||
<h1 className="text-primary">{pageTitle}</h1>
|
||||
{typeof pageSubtitle === 'string'
|
||||
? <h3><Badge className="py-2 px-3 font-weight-normal" variant="light">{pageSubtitle}</Badge></h3>
|
||||
: pageSubtitle}
|
||||
</Col>
|
||||
<Col xs={12} md={4}>
|
||||
<div className="d-flex justify-content-md-end">
|
||||
{
|
||||
actions.map(({ label, onClick }) => <Button key={`authz-header-action-${label}`} onClick={onClick}>{label}</Button>)
|
||||
}
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
|
||||
export default AuthZTitle;
|
||||
6
src/authz-module/index.scss
Normal file
6
src/authz-module/index.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
.authz-libraries {
|
||||
.pgn__breadcrumb li:first-child a {
|
||||
color: var(--pgn-color-breadcrumb-active);
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user