MM-50252_Refresh UI of the marketplace to improve first impressions (#22698)

Этот коммит содержится в:
Julian Mondragón
2023-04-03 12:10:39 -05:00
коммит произвёл GitHub
родитель 69657be9f2
Коммит 22bc41effa
43 изменённых файлов: 1890 добавлений и 1652 удалений

Просмотреть файл

@@ -0,0 +1,41 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/GenericModal/FooterPagination should render default 1`] = `
<div
className="footer-pagination"
>
<div
className="footer-pagination__legend"
/>
<div
className="footer-pagination__button-container"
>
<button
className="footer-pagination__button-container__button disabled"
disabled={true}
onClick={[MockFunction]}
type="button"
>
<ChevronLeftIcon
size={16}
/>
<span>
Previous
</span>
</button>
<button
className="footer-pagination__button-container__button disabled"
disabled={true}
onClick={[MockFunction]}
type="button"
>
<span>
Next
</span>
<ChevronRightIcon
size={16}
/>
</button>
</div>
</div>
`;

Просмотреть файл

@@ -0,0 +1,41 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
@import '../../../../../channels/src/sass/utils/mixins';
.footer-pagination {
display: flex;
flex: 1;
flex-direction: row;
align-items: center;
&__legend {
display: flex;
flex: 1;
align-items: center;
justify-content: flex-start;
color: rgba(var(--center-channel-color-rgb), 0.64);
font-size: 12px;
font-weight: 600;
line-height: 16px;
}
&__button-container {
display: flex;
align-items: center;
justify-content: center;
&__button {
@include tertiary-button;
@include button-small;
&:not(:first-child) {
margin-left: 8px;
}
> :not(:first-child) {
margin-left: 5px;
}
}
}
}

Просмотреть файл

@@ -0,0 +1,88 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {shallow} from 'enzyme';
import {FooterPagination} from './';
describe('components/GenericModal/FooterPagination', () => {
const baseProps = {
page: 0,
total: 0,
itemsPerPage: 0,
onNextPage: jest.fn(),
onPreviousPage: jest.fn(),
};
test('should render default', () => {
const wrapper = shallow(
<FooterPagination {...baseProps}/>,
);
expect(wrapper).toMatchSnapshot();
});
test('should render pagination legend', () => {
const wrapper = shallow(
<FooterPagination
{...baseProps}
page={0}
total={17}
itemsPerPage={10}
/>,
);
const legend = wrapper.find('.footer-pagination__legend');
expect(legend.length).toEqual(1);
expect(legend.at(0).text()).toEqual('Showing 1-10 of 17');
});
test('should render pagination buttons', () => {
const wrapper = shallow(
<FooterPagination
{...baseProps}
page={1}
total={30}
itemsPerPage={10}
/>,
);
const buttons = wrapper.find('.footer-pagination__button-container__button');
expect(buttons.length).toEqual(2);
expect(buttons.at(0).text()).toEqual('<ChevronLeftIcon />Previous');
expect(buttons.at(1).text()).toEqual('Next<ChevronRightIcon />');
});
test('should handle pagination buttons', async () => {
const onPreviousPage = jest.fn();
const onNextPage = jest.fn();
const wrapper = shallow(
<FooterPagination
page={1}
total={30}
itemsPerPage={10}
onPreviousPage={onPreviousPage}
onNextPage={onNextPage}
/>,
);
const buttons = wrapper.find('.footer-pagination__button-container__button');
const prevButton = buttons.at(0);
const nextButton = buttons.at(1);
expect(prevButton.hasClass('disabled')).toBeFalsy();
expect(nextButton.hasClass('disabled')).toBeFalsy();
nextButton.simulate('click');
expect(onNextPage).toHaveBeenCalledTimes(1);
prevButton.simulate('click');
expect(onPreviousPage).toHaveBeenCalledTimes(1);
});
});

Просмотреть файл

@@ -0,0 +1,93 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import classNames from 'classnames';
import React from 'react';
import {useIntl} from 'react-intl';
import {ChevronLeftIcon, ChevronRightIcon} from '@mattermost/compass-icons/components';
import './footer_pagination.scss';
const BUTTON_ICON_SIZE = 16;
type FooterPaginationProps = {
page: number;
total: number;
itemsPerPage: number;
onNextPage: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
onPreviousPage: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
};
export const FooterPagination = ({
page,
total,
itemsPerPage,
onNextPage,
onPreviousPage,
}: FooterPaginationProps) => {
const {formatMessage} = useIntl();
const startCount = page * itemsPerPage;
const endCount = Math.min(startCount + itemsPerPage, total);
const totalPages = Math.trunc((total - 1) / itemsPerPage);
const prevDisabled = page <= 0;
const nextDisabled = page >= totalPages;
return (
<div className='footer-pagination'>
<div className='footer-pagination__legend'>
{Boolean(total) && (
formatMessage(
{
id: 'footer_pagination.count',
defaultMessage: 'Showing {startCount, number}-{endCount, number} of {total, number}',
},
{
startCount: startCount + 1,
endCount,
total,
},
)
)}
</div>
<div className='footer-pagination__button-container'>
<button
type='button'
className={classNames(
'footer-pagination__button-container__button',
{disabled: prevDisabled},
)}
onClick={onPreviousPage}
disabled={prevDisabled}
>
<ChevronLeftIcon size={BUTTON_ICON_SIZE}/>
<span>
{formatMessage({
id: 'footer_pagination.prev',
defaultMessage: 'Previous',
})}
</span>
</button>
<button
type='button'
className={classNames(
'footer-pagination__button-container__button',
{disabled: nextDisabled},
)}
onClick={onNextPage}
disabled={nextDisabled}
>
<span>
{formatMessage({
id: 'footer_pagination.next',
defaultMessage: 'Next',
})}
</span>
<ChevronRightIcon size={BUTTON_ICON_SIZE}/>
</button>
</div>
</div>
);
};

Просмотреть файл

@@ -0,0 +1,4 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
export * from './footer_pagination';

Просмотреть файл

@@ -34,13 +34,17 @@ export type Props = {
enforceFocus?: boolean;
container?: React.ReactNode | React.ReactNodeArray;
ariaLabel?: string;
errorText?: string;
errorText?: string | React.ReactNode;
compassDesign?: boolean;
backdrop?: boolean;
backdropClassName?: string;
tabIndex?: number;
children: React.ReactNode;
keyboardEscape?: boolean;
headerInput?: React.ReactNode;
bodyPadding?: boolean;
footerContent?: React.ReactNode;
footerDivider?: boolean;
};
type State = {
@@ -56,6 +60,7 @@ export class GenericModal extends React.PureComponent<Props, State> {
autoCloseOnConfirmButton: true,
enforceFocus: true,
keyboardEscape: true,
bodyPadding: true,
};
constructor(props: Props) {
@@ -195,7 +200,12 @@ export class GenericModal extends React.PureComponent<Props, State> {
className='GenericModal__wrapper-enter-key-press-catcher'
>
<Modal.Header closeButton={true}>
{this.props.compassDesign && headerText}
{this.props.compassDesign && (
<>
{headerText}
{this.props.headerInput}
</>
)}
</Modal.Header>
<Modal.Body>
{this.props.compassDesign ? (
@@ -208,14 +218,22 @@ export class GenericModal extends React.PureComponent<Props, State> {
) : (
headerText
)}
<div className='GenericModal__body'>
<div className={classNames('GenericModal__body', {padding: this.props.bodyPadding})}>
{this.props.children}
</div>
</Modal.Body>
{(cancelButton || confirmButton) && <Modal.Footer>
{cancelButton}
{confirmButton}
</Modal.Footer>}
{(cancelButton || confirmButton || this.props.footerContent) && (
<Modal.Footer className={classNames({divider: this.props.footerDivider})}>
{(cancelButton || confirmButton) ? (
<>
{cancelButton}
{confirmButton}
</>
) : (
this.props.footerContent
)}
</Modal.Footer>
)}
</div>
</FocusTrap>
</Modal>

Просмотреть файл

@@ -7,6 +7,7 @@ export type {CircleSkeletonLoaderProps, RectangleSkeletonLoaderProps} from './sk
export type {Props as FocusTrapProps} from './focus_trap';
// components
export * from './generic_modal/footer_content';
export {GenericModal} from './generic_modal/generic_modal';
export {CircleSkeletonLoader, RectangleSkeletonLoader} from './skeleton_loader';
export * from './tour_tip';