mirror of
https://github.com/portainer/portainer.git
synced 2025-07-25 00:09:40 +02:00
fix(ui): fix ui bugs [EE-3847] (#7453)
This commit is contained in:
parent
dd372637cb
commit
95fb5a4baa
43 changed files with 246 additions and 448 deletions
|
@ -1,12 +0,0 @@
|
|||
.breadcrumb-links {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.breadcrumb-links a {
|
||||
color: var(--ui-blue-8);
|
||||
}
|
||||
|
||||
.breadcrumb-links a:hover {
|
||||
text-decoration: underline;
|
||||
color: var(--ui-blue-9);
|
||||
}
|
|
@ -8,8 +8,9 @@ test('should display a Breadcrumbs, breadcrumbs should be separated by >', async
|
|||
{ label: 'bread2' },
|
||||
{ label: 'bread3' },
|
||||
];
|
||||
const { queryByText } = render(<Breadcrumbs breadcrumbs={breadcrumbs} />);
|
||||
const { container } = render(<Breadcrumbs breadcrumbs={breadcrumbs} />);
|
||||
|
||||
const heading = queryByText(breadcrumbs.map((b) => b.label).join(' > '));
|
||||
expect(heading).toBeVisible();
|
||||
expect(container.firstChild?.textContent).toEqual(
|
||||
breadcrumbs.map((b) => b.label).join('>')
|
||||
);
|
||||
});
|
||||
|
|
|
@ -2,8 +2,6 @@ import { Fragment } from 'react';
|
|||
|
||||
import { Link } from '@@/Link';
|
||||
|
||||
import './Breadcrumbs.css';
|
||||
|
||||
export interface Crumb {
|
||||
label: string;
|
||||
link?: string;
|
||||
|
@ -19,11 +17,11 @@ export function Breadcrumbs({ breadcrumbs }: Props) {
|
|||
: [breadcrumbs];
|
||||
|
||||
return (
|
||||
<div className="breadcrumb-links">
|
||||
<div className="text-sm font-medium text-gray-7 th-dark:text-gray-5 th-highcontrast:text-white space-x-2">
|
||||
{breadcrumbsArray.map((crumb, index) => (
|
||||
<Fragment key={index}>
|
||||
{renderCrumb(crumb)}
|
||||
{index !== breadcrumbsArray.length - 1 ? ' > ' : ''}
|
||||
<span>{renderCrumb(crumb)}</span>
|
||||
{index !== breadcrumbsArray.length - 1 && <span>></span>}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
@ -44,7 +42,7 @@ function renderCrumb(crumb: Crumb | string) {
|
|||
<Link
|
||||
to={crumb.link}
|
||||
params={crumb.linkParams}
|
||||
className="text-blue-9 hover:underline"
|
||||
className="text-blue-9 hover:underline hover:text-blue-11 th-dark:text-blue-7 th-dark:hover:text-blue-9 th-highcontrast:text-blue-5"
|
||||
>
|
||||
{crumb.label}
|
||||
</Link>
|
||||
|
|
|
@ -1,33 +1,29 @@
|
|||
.row.header .meta .page {
|
||||
padding-top: 7px;
|
||||
.header {
|
||||
min-height: 60px;
|
||||
margin-bottom: 15px;
|
||||
|
||||
background-color: var(--bg-body-color);
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
|
||||
.row.header {
|
||||
min-height: 60px;
|
||||
background: var(--bg-row-header-color);
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.row.header > div:last-child {
|
||||
.header > div:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
.row.header .meta .page {
|
||||
font-size: 17px;
|
||||
padding-top: 11px;
|
||||
}
|
||||
|
||||
.row.header .meta div {
|
||||
.header .meta div {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.row.header .login a {
|
||||
|
||||
.header .login a {
|
||||
padding: 18px;
|
||||
display: block;
|
||||
}
|
||||
.row.header .user {
|
||||
.header .user {
|
||||
min-width: 130px;
|
||||
}
|
||||
.row.header .user > .item {
|
||||
.header .user > .item {
|
||||
width: 65px;
|
||||
height: 60px;
|
||||
float: right;
|
||||
|
@ -35,36 +31,36 @@
|
|||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.row.header .user > .item a {
|
||||
.header .user > .item a {
|
||||
color: #919191;
|
||||
display: block;
|
||||
}
|
||||
.row.header .user > .item i {
|
||||
.header .user > .item i {
|
||||
font-size: 20px;
|
||||
line-height: 55px;
|
||||
}
|
||||
.row.header .user > .item img {
|
||||
.header .user > .item img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-top: 10px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.row.header .user > .item ul.dropdown-menu {
|
||||
.header .user > .item ul.dropdown-menu {
|
||||
border-radius: 2px;
|
||||
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.05);
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.row.header .user > .item ul.dropdown-menu .dropdown-header {
|
||||
.header .user > .item ul.dropdown-menu .dropdown-header {
|
||||
text-align: center;
|
||||
}
|
||||
.row.header .user > .item ul.dropdown-menu li.link {
|
||||
.header .user > .item ul.dropdown-menu li.link {
|
||||
text-align: left;
|
||||
}
|
||||
.row.header .user > .item ul.dropdown-menu li.link a {
|
||||
.header .user > .item ul.dropdown-menu li.link a {
|
||||
padding-left: 7px;
|
||||
padding-right: 7px;
|
||||
}
|
||||
.row.header .user > .item ul.dropdown-menu:before {
|
||||
.header .user > .item ul.dropdown-menu:before {
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
right: 23px;
|
||||
|
@ -74,7 +70,7 @@
|
|||
border-left: 7px solid transparent;
|
||||
content: '';
|
||||
}
|
||||
.row.header .user > .item ul.dropdown-menu:after {
|
||||
.header .user > .item ul.dropdown-menu:after {
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
right: 24px;
|
|
@ -7,7 +7,6 @@ import { UserViewModel } from '@/portainer/models/user';
|
|||
import { HeaderContainer } from './HeaderContainer';
|
||||
import { Breadcrumbs } from './Breadcrumbs';
|
||||
import { HeaderTitle } from './HeaderTitle';
|
||||
import { HeaderContent } from './HeaderContent';
|
||||
|
||||
export default {
|
||||
component: HeaderContainer,
|
||||
|
@ -28,14 +27,13 @@ function Template({ title }: StoryProps) {
|
|||
<UserContext.Provider value={state}>
|
||||
<HeaderContainer>
|
||||
<HeaderTitle title={title} />
|
||||
<HeaderContent>
|
||||
<Breadcrumbs
|
||||
breadcrumbs={[
|
||||
{ link: 'example', label: 'crumb1' },
|
||||
{ label: 'crumb2' },
|
||||
]}
|
||||
/>
|
||||
</HeaderContent>
|
||||
|
||||
<Breadcrumbs
|
||||
breadcrumbs={[
|
||||
{ link: 'example', label: 'crumb1' },
|
||||
{ label: 'crumb2' },
|
||||
]}
|
||||
/>
|
||||
</HeaderContainer>
|
||||
</UserContext.Provider>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { PropsWithChildren, createContext, useContext } from 'react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import './HeaderContainer.css';
|
||||
import styles from './HeaderContainer.module.css';
|
||||
|
||||
const Context = createContext<null | boolean>(null);
|
||||
|
||||
|
@ -15,10 +16,10 @@ export function useHeaderContext() {
|
|||
export function HeaderContainer({ children }: PropsWithChildren<unknown>) {
|
||||
return (
|
||||
<Context.Provider value>
|
||||
<div className="row header">
|
||||
<div className={clsx('row', styles.header)}>
|
||||
<div id="loadingbar-placeholder" />
|
||||
<div className="col-xs-12">
|
||||
<div className="meta">{children}</div>
|
||||
<div className={styles.meta}>{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Context.Provider>
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
import { renderWithQueryClient } from '@/react-tools/test-utils';
|
||||
|
||||
import { HeaderContainer } from './HeaderContainer';
|
||||
import { HeaderContent } from './HeaderContent';
|
||||
|
||||
test('should not render without a wrapping HeaderContainer', async () => {
|
||||
const consoleErrorFn = jest
|
||||
.spyOn(console, 'error')
|
||||
.mockImplementation(() => jest.fn());
|
||||
|
||||
function renderComponent() {
|
||||
return renderWithQueryClient(<HeaderContent />);
|
||||
}
|
||||
|
||||
expect(renderComponent).toThrowErrorMatchingSnapshot();
|
||||
|
||||
consoleErrorFn.mockRestore();
|
||||
});
|
||||
|
||||
test('should display a HeaderContent', async () => {
|
||||
const content = 'content';
|
||||
|
||||
const { queryByText } = renderWithQueryClient(
|
||||
<HeaderContainer>
|
||||
<HeaderContent>{content}</HeaderContent>
|
||||
</HeaderContainer>
|
||||
);
|
||||
|
||||
const contentElement = queryByText(content);
|
||||
expect(contentElement).toBeVisible();
|
||||
});
|
|
@ -1,13 +0,0 @@
|
|||
import { PropsWithChildren } from 'react';
|
||||
|
||||
import { useHeaderContext } from './HeaderContainer';
|
||||
|
||||
export function HeaderContent({ children }: PropsWithChildren<unknown>) {
|
||||
useHeaderContext();
|
||||
|
||||
return (
|
||||
<div className="breadcrumb-links">
|
||||
<div className="pull-left">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -24,13 +24,17 @@ export function HeaderTitle({ title, children }: PropsWithChildren<Props>) {
|
|||
const { user } = useUser();
|
||||
|
||||
return (
|
||||
<div className="page white-space-normal">
|
||||
{title}
|
||||
<span className="header_title_content">{children}</span>
|
||||
<div className="flex justify-between whitespace-normal pt-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="font-medium text-3xl text-gray-11 th-dark:text-white th-highcontrast:text-white">
|
||||
{title}
|
||||
</div>
|
||||
{children && <span>{children}</span>}
|
||||
</div>
|
||||
<Menu>
|
||||
<MenuButton
|
||||
className={clsx(
|
||||
'pull-right flex items-center gap-1',
|
||||
'ml-auto flex items-center gap-1 self-start',
|
||||
styles.menuButton
|
||||
)}
|
||||
data-cy="userMenu-button"
|
||||
|
|
|
@ -6,7 +6,6 @@ import { Button } from '../buttons';
|
|||
import { Breadcrumbs } from './Breadcrumbs';
|
||||
import { Crumb } from './Breadcrumbs/Breadcrumbs';
|
||||
import { HeaderContainer } from './HeaderContainer';
|
||||
import { HeaderContent } from './HeaderContent';
|
||||
import { HeaderTitle } from './HeaderTitle';
|
||||
import styles from './PageHeader.module.css';
|
||||
|
||||
|
@ -33,9 +32,8 @@ export function PageHeader({
|
|||
|
||||
return (
|
||||
<HeaderContainer>
|
||||
<HeaderContent>
|
||||
<Breadcrumbs breadcrumbs={breadcrumbs} />
|
||||
</HeaderContent>
|
||||
<Breadcrumbs breadcrumbs={breadcrumbs} />
|
||||
|
||||
<HeaderTitle title={title}>
|
||||
{reload && (
|
||||
<Button
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`should not render without a wrapping HeaderContainer 1`] = `"Should be nested inside a HeaderContainer component"`;
|
|
@ -1,7 +1 @@
|
|||
import { Breadcrumbs } from './Breadcrumbs';
|
||||
import { PageHeader } from './PageHeader';
|
||||
import { HeaderContainer } from './HeaderContainer';
|
||||
import { HeaderContent } from './HeaderContent';
|
||||
import { HeaderTitle } from './HeaderTitle';
|
||||
|
||||
export { PageHeader, Breadcrumbs, HeaderContainer, HeaderContent, HeaderTitle };
|
||||
export { PageHeader } from './PageHeader';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue