Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update cost models settings layout per mocks #3329

Merged
merged 1 commit into from
Aug 10, 2023
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
10 changes: 8 additions & 2 deletions locales/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -1376,10 +1376,10 @@
"value": "Should not exceed 100 characters"
}
],
"costModelsLastChange": [
"costModelsLastUpdated": [
{
"type": 0,
"value": "Last change"
"value": "Last updated"
}
],
"costModelsPopover": [
Expand Down Expand Up @@ -11230,6 +11230,12 @@
"value": "Failed to get RBAC information"
}
],
"readOnly": [
{
"type": 0,
"value": "Read only"
}
],
"readOnlyPermissions": [
{
"type": 0,
Expand Down
3 changes: 2 additions & 1 deletion locales/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
"costModelsFilterPlaceholder": "Filter by name...",
"costModelsFilterTagKey": "Filter by tag key",
"costModelsInfoTooLong": "Should not exceed 100 characters",
"costModelsLastChange": "Last change",
"costModelsLastUpdated": "Last updated",
"costModelsPopover": "A cost model allows you to associate a price to metrics provided by your sources to charge for utilization of resources. {learnMore}",
"costModelsPopoverAriaLabel": "Cost model info popover",
"costModelsPopoverButtonAriaLabel": "Opens a dialog with cost model info",
Expand Down Expand Up @@ -484,6 +484,7 @@
"rawCostTitle": "Raw cost",
"rbacErrorDesc": "There was a problem receiving user permissions. Refreshing this page may fix it. If it does not, please contact your admin.",
"rbacErrorTitle": "Failed to get RBAC information",
"readOnly": "Read only",
"readOnlyPermissions": "You have read only permissions",
"recommended": "Recommended",
"redHatStatusUrl": "https://status.redhat.com",
Expand Down
5 changes: 3 additions & 2 deletions src/__mocks__/react-intl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@ const mockedReactIntl = jest.genMockFromModule('react-intl') as any;
const intl = {
formatDate: jest.fn(() => ''), // Dates won't match snapshots during PR builds
formatDateTimeRange: jest.fn(() => ''), // Dates won't match snapshots during PR builds
formatMessage: ({ defaultMessage }, params?) => {
formatMessage: jest.fn(({ defaultMessage }, params?) => {
if (!params) {
return defaultMessage;
}
return defaultMessage + JSON.stringify(params);
},
}),
formatNumber: jest.fn(v => v),
};

mockedReactIntl.createIntl = () => intl;
mockedReactIntl.defineMessages = jest.fn(v => v);
mockedReactIntl.injectIntl = jest.fn(v => v);
// mockedReactIntl.injectIntl = Component => props => <Component {...props} intl={intl} />;
mockedReactIntl.useIntl = () => intl;

module.exports = mockedReactIntl;
13 changes: 9 additions & 4 deletions src/locales/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -610,10 +610,10 @@ export default defineMessages({
description: 'Should not exceed 100 characters',
id: 'costModelsInfoTooLong',
},
costModelsLastChange: {
defaultMessage: 'Last change',
description: 'Last change',
id: 'costModelsLastChange',
costModelsLastUpdated: {
defaultMessage: 'Last updated',
description: 'Last updated',
id: 'costModelsLastUpdated',
},
costModelsPopover: {
defaultMessage:
Expand Down Expand Up @@ -3014,6 +3014,11 @@ export default defineMessages({
description: 'RBAC error title',
id: 'rbacErrorTitle',
},
readOnly: {
defaultMessage: 'Read only',
description: 'Read only',
id: 'readOnly',
},
readOnlyPermissions: {
defaultMessage: 'You have read only permissions',
description: 'You have read only permissions',
Expand Down
2 changes: 1 addition & 1 deletion src/routes/costModels/costModelsDetails/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class CostModelsTableBase extends React.Component<CostModelsTableProps, CostMode
},
{ title: intl.formatMessage(messages.costModelsAssignedSources) },
{
title: intl.formatMessage(messages.costModelsLastChange),
title: intl.formatMessage(messages.costModelsLastUpdated),
data: { orderName: 'updated_timestamp' },
...(rows.length && { transforms: [sortable] }),
},
Expand Down
17 changes: 10 additions & 7 deletions src/routes/settings/costModels/components/readOnlyTooltip.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import { Tooltip } from '@patternfly/react-core';
import messages from 'locales/messages';
import React from 'react';
import { useIntl } from 'react-intl';

interface ReadOnlyTooltipBase {
tooltip?: string;
children: JSX.Element;
isDisabled: boolean;
}

export const ReadOnlyTooltip: React.FC<ReadOnlyTooltipBase> = ({
children,
tooltip = 'You have read only permissions',
isDisabled,
}) => {
const ReadOnlyTooltip: React.FC<ReadOnlyTooltipBase> = ({ children, tooltip, isDisabled }) => {
const intl = useIntl();
const content = tooltip ? tooltip : intl.formatMessage(messages.readOnlyPermissions);

return isDisabled ? (
<Tooltip isContentLeftAligned content={<div>{tooltip}</div>}>
<div aria-label="Read only">{children}</div>
<Tooltip isContentLeftAligned content={<div>{content}</div>}>
<div aria-label={intl.formatMessage(messages.readOnly)}>{children}</div>
</Tooltip>
) : (
children
);
};

export { ReadOnlyTooltip };
2 changes: 1 addition & 1 deletion src/routes/settings/costModels/costModel/distribution.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ const DistributionCardBase: React.FC<Props> = ({
<ReadOnlyTooltip key="edit" isDisabled={!isWritePermission}>
<Button
aria-label={intl.formatMessage(messages.costModelsDistributionEdit)}
variant={ButtonVariant.link}
isAriaDisabled={!isWritePermission}
onClick={() => setCostModelDialog({ isOpen: true, name: 'updateDistribution' })}
variant={ButtonVariant.link}
>
{intl.formatMessage(messages.edit)}
</Button>
Expand Down
47 changes: 30 additions & 17 deletions src/routes/settings/costModels/costModel/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import type { CostModel } from 'api/costModels';
import messages from 'locales/messages';
import React from 'react';
import type { WrappedComponentProps } from 'react-intl';
import { injectIntl } from 'react-intl';
import { useIntl } from 'react-intl';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { routes } from 'routes';
Expand Down Expand Up @@ -54,7 +54,6 @@ const Header: React.FC<Props> = ({
current,
deleteCostModel,
deleteError,
intl,
isDeleteProcessing,
isDialogOpen,
isWritePermission,
Expand All @@ -65,6 +64,18 @@ const Header: React.FC<Props> = ({
tabIndex,
}) => {
const [dropdownIsOpen, setDropdownIsOpen] = React.useState(false);
const intl = useIntl();

const dateTime: any = intl.formatDate(current.updated_timestamp, {
day: 'numeric',
hour: 'numeric',
hour12: false,
minute: 'numeric',
month: 'short',
timeZone: 'UTC',
timeZoneName: 'short',
year: 'numeric',
});

return (
<>
Expand Down Expand Up @@ -161,6 +172,10 @@ const Header: React.FC<Props> = ({
</Split>
<TextContent style={styles.currency}>
<TextList component={TextListVariants.dl}>
<TextListItem component={TextListItemVariants.dt}>
{intl.formatMessage(messages.costModelsLastUpdated)}
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>{dateTime}</TextListItem>
<TextListItem component={TextListItemVariants.dt}>{intl.formatMessage(messages.currency)}</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{intl.formatMessage(messages.currencyOptions, { units: current.currency || 'USD' })}
Expand Down Expand Up @@ -209,19 +224,17 @@ const Header: React.FC<Props> = ({
);
};

export default injectIntl(
withRouter(
connect(
createMapStateToProps(state => ({
isDialogOpen: costModelsSelectors.isDialogOpen(state)('costmodel'),
isDeleteProcessing: costModelsSelectors.deleteProcessing(state),
deleteError: costModelsSelectors.deleteError(state),
isWritePermission: rbacSelectors.isCostModelWritePermission(state),
})),
{
setDialogOpen: costModelsActions.setCostModelDialog,
deleteCostModel: costModelsActions.deleteCostModel,
}
)(Header)
)
export default withRouter(
connect(
createMapStateToProps(state => ({
isDialogOpen: costModelsSelectors.isDialogOpen(state)('costmodel'),
isDeleteProcessing: costModelsSelectors.deleteProcessing(state),
deleteError: costModelsSelectors.deleteError(state),
isWritePermission: rbacSelectors.isCostModelWritePermission(state),
})),
{
setDialogOpen: costModelsActions.setCostModelDialog,
deleteCostModel: costModelsActions.deleteCostModel,
}
)(Header)
);
2 changes: 1 addition & 1 deletion src/routes/settings/costModels/costModel/markup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ const MarkupCardBase: React.FC<Props> = ({
<ReadOnlyTooltip key="edit" isDisabled={!isWritePermission}>
<Button
aria-label={intl.formatMessage(messages.editMarkup)}
variant={ButtonVariant.link}
isAriaDisabled={!isWritePermission}
onClick={() => setCostModelDialog({ isOpen: true, name: 'updateMarkup' })}
variant={ButtonVariant.link}
>
{intl.formatMessage(messages.edit)}
</Button>
Expand Down
46 changes: 16 additions & 30 deletions src/routes/settings/costModels/costModel/sourcesTable.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import type { IAction, IRow } from '@patternfly/react-table';
import { ActionsColumn, TableComposable, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table';
import { Button, ButtonVariant } from '@patternfly/react-core';
import { MinusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/minus-circle-icon';
import type { IRow } from '@patternfly/react-table';
import { TableComposable, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table';
import { TableGridBreakpoint } from '@patternfly/react-table';
import messages from 'locales/messages';
import React from 'react';
import type { WrappedComponentProps } from 'react-intl';
import { injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import { ReadOnlyTooltip } from 'routes/settings/costModels/components/readOnlyTooltip';
import { createMapStateToProps } from 'store/common';
import { costModelsSelectors } from 'store/costModels';
import { rbacSelectors } from 'store/rbac';
Expand All @@ -22,26 +25,6 @@ interface SourcesTableStateProps {
type SourcesTableProps = SourcesTableOwnProps & SourcesTableStateProps & WrappedComponentProps;

const SourcesTable: React.FC<SourcesTableProps> = ({ canWrite, costModels, intl, showDeleteDialog }) => {
const getActions = (): IAction[] => {
if (canWrite) {
return [
{
title: intl.formatMessage(messages.costModelsSourceDelete),
onClick: (_evt, rowIndex: number) => showDeleteDialog(rowIndex),
},
];
}
return [
{
style: { pointerEvents: 'auto' },
tooltip: intl.formatMessage(messages.readOnlyPermissions),
isDisabled: true,
title: intl.formatMessage(messages.costModelsSourceDelete),
},
];
};

const actions = getActions();
const rows: (IRow | string[])[] = costModels.length > 0 ? costModels[0].sources.map(source => [source.name]) : [];

return (
Expand All @@ -61,14 +44,17 @@ const SourcesTable: React.FC<SourcesTableProps> = ({ canWrite, costModels, intl,
<Tr key={rowIndex}>
<Td>{r}</Td>
<Td isActionCell>
<ActionsColumn
items={actions.map(a => {
return {
...a,
onClick: () => a.onClick(null, rowIndex, r, null),
};
})}
></ActionsColumn>
<ReadOnlyTooltip key="action" isDisabled={!canWrite}>
<Button
aria-label={intl.formatMessage(messages.costModelsSourceDelete)}
isAriaDisabled={!canWrite}
isSmall
onClick={() => showDeleteDialog(rowIndex)}
variant={ButtonVariant.plain}
>
<MinusCircleIcon />
</Button>
</ReadOnlyTooltip>
</Td>
</Tr>
))}
Expand Down
42 changes: 19 additions & 23 deletions src/routes/settings/costModels/costModelsDetails/table.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { ICell, IRowData, ThProps } from '@patternfly/react-table';
import { ActionsColumn, TableComposable, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table';
import { Button, ButtonVariant } from '@patternfly/react-core';
import { MinusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/minus-circle-icon';
import type { ICell, ThProps } from '@patternfly/react-table';
import { TableComposable, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table';
import { sortable, TableGridBreakpoint } from '@patternfly/react-table';
import type { CostModel } from 'api/costModels';
import { intl as defaultIntl } from 'components/i18n';
Expand All @@ -9,14 +11,15 @@ import type { WrappedComponentProps } from 'react-intl';
import { injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import type { Dispatch } from 'redux';
import { ReadOnlyTooltip } from 'routes/settings/costModels/components/readOnlyTooltip';
import { createMapStateToProps } from 'store/common';
import { costModelsActions, costModelsSelectors } from 'store/costModels';
import { rbacSelectors } from 'store/rbac';
import type { RouterComponentProps } from 'utils/router';
import { withRouter } from 'utils/router';

import type { CostModelsQuery } from './utils/query';
import { createActions, createOnSort, getRowsByStateName } from './utils/table';
import { createOnSort, getRowsByStateName } from './utils/table';

interface CostModelsTableOwnProps {
actionResolver?: any;
Expand Down Expand Up @@ -75,7 +78,7 @@ class CostModelsTableBase extends React.Component<CostModelsTableProps, CostMode
},
{ title: intl.formatMessage(messages.costModelsAssignedSources) },
{
title: intl.formatMessage(messages.costModelsLastChange),
title: intl.formatMessage(messages.costModelsLastUpdated),
data: { orderName: 'updated_timestamp' },
...(rows.length && { transforms: [sortable] }),
},
Expand All @@ -85,16 +88,6 @@ class CostModelsTableBase extends React.Component<CostModelsTableProps, CostMode
},
] as ICell[];

const actions = createActions(stateName, canWrite, [
{
title: intl.formatMessage(messages.delete),
tooltip: intl.formatMessage(messages.readOnlyPermissions),
onClick: (_evt: React.MouseEvent, _rowIx: number, rowData: IRowData) => {
openDeleteDialog(rowData.data);
},
} as any,
]);

const onSort = createOnSort(cells, query, router);
const getSortParams = (columnIndex: number): ThProps['sort'] => ({
sortBy: {
Expand Down Expand Up @@ -137,16 +130,19 @@ class CostModelsTableBase extends React.Component<CostModelsTableProps, CostMode
{c.title ? c.title : c}
</Td>
))}
{!r.heightAuto && (
{!r.heightAuto && stateName === 'success' && (
<Td isActionCell>
<ActionsColumn
items={actions.map(a => {
return {
...a,
onClick: _evt => a.onClick(_evt, rowIndex, r, null),
};
})}
/>
<ReadOnlyTooltip key="action" isDisabled={!canWrite}>
<Button
aria-label={intl.formatMessage(messages.delete)}
isAriaDisabled={!canWrite}
isSmall
onClick={() => openDeleteDialog(r.data)}
variant={ButtonVariant.plain}
>
<MinusCircleIcon />
</Button>
</ReadOnlyTooltip>
</Td>
)}
</Tr>
Expand Down
Loading