Skip to content

Commit

Permalink
feat(pipelines-output): add pipelinerun output tab component (#7)
Browse files Browse the repository at this point in the history
* feat(pipelines-output): add pipelinerun output tab component

* update peer deps and remove sample button component

* update date format
  • Loading branch information
karthikjeeyar authored Jan 24, 2024
1 parent bbe65e5 commit 7bbd9b4
Show file tree
Hide file tree
Showing 98 changed files with 9,174 additions and 1,525 deletions.
34 changes: 34 additions & 0 deletions docs/stories/pipelines/AdvancedCluterSecurity.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import '@patternfly/react-core/dist/styles/base.css';
import { AdvancedClusterSecurity } from '@aonic-ui/pipelines/src';
import {
acsDeploymentCheck as acsDeploymentCheckResults,
acsImageCheckResults,
acsImageScanResult,
} from '@aonic-ui/pipelines/src/components/Output/data';
import type { Meta, StoryObj } from '@storybook/react';

// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
const meta = {
title: 'Pipelines/Output/Cards/AdvancedClusterSecurity',
component: AdvancedClusterSecurity,
parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
layout: 'padded',
},
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
tags: ['autodocs'],
// More on argTypes: https://storybook.js.org/docs/api/argtypes
argTypes: {},
} satisfies Meta<typeof AdvancedClusterSecurity>;
export default meta;

type Story = StoryObj<typeof meta>;

export const ACSCard: Story = {
args: {
acsImageCheckResults,
acsImageScanResult,
acsDeploymentCheckResults,
},
parameters: {},
};
30 changes: 30 additions & 0 deletions docs/stories/pipelines/EnterpriseContract.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import '@patternfly/react-core/dist/styles/base.css';
import { EnterpriseContract } from '@aonic-ui/pipelines/src';
import { mockEnterpriseContractUIData } from '@aonic-ui/pipelines/src/components/Output/data';
import type { Meta, StoryObj } from '@storybook/react';

// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
const meta = {
title: 'Pipelines/Output/Cards/EnterpriseContract',
component: EnterpriseContract,
parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
layout: 'padded',
},
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
tags: ['autodocs'],
// More on argTypes: https://storybook.js.org/docs/api/argtypes
argTypes: {
enterpriseContractPolicies: {},
},
} satisfies Meta<typeof EnterpriseContract>;
export default meta;

type Story = StoryObj<typeof meta>;

export const ECCard: Story = {
args: {
enterpriseContractPolicies: mockEnterpriseContractUIData,
},
parameters: {},
};
72 changes: 72 additions & 0 deletions docs/stories/pipelines/Output.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import '@patternfly/react-core/dist/styles/base.css';
import { Output } from '@aonic-ui/pipelines/src';
import {
acsDeploymentCheck,
acsImageCheckResults,
acsImageScanResult,
mockEnterpriseContractUIData,
} from '@aonic-ui/pipelines/src/components/Output/data';
import type { Meta, StoryObj } from '@storybook/react';

// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
const meta = {
title: 'Pipelines/Output',
component: Output,
parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
layout: 'padded',
},
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
tags: ['autodocs'],
// More on argTypes: https://storybook.js.org/docs/api/argtypes
argTypes: {
pipelineRunStatus: { control: 'select', options: ['Failed', 'Succeeded'] },
},
} satisfies Meta<typeof Output>;
export default meta;

type Story = StoryObj<typeof meta>;

export const OutputTab: Story = {
args: {
acsImageScanResult,
acsImageCheckResults,
enterpriseContractPolicies: mockEnterpriseContractUIData,
acsDeploymentCheckResults: acsDeploymentCheck,
results: [{ name: 'result-1', value: 'value' }],
pipelineRunName: 'pipelineRunName',
pipelineRunStatus: 'Succeeded',
},
parameters: {},
};

export const OutputTabEC: Story = {
args: {
enterpriseContractPolicies: mockEnterpriseContractUIData,
results: [{ name: 'result-1', value: 'value' }],
pipelineRunName: 'pipelineRunName',
pipelineRunStatus: 'Succeeded',
},
parameters: {},
};

export const OutputTabACS: Story = {
args: {
acsImageScanResult,
acsImageCheckResults,
acsDeploymentCheckResults: acsDeploymentCheck,
results: [{ name: 'result-1', value: 'value' }],
pipelineRunName: 'pipelineRunName',
pipelineRunStatus: 'Succeeded',
},
parameters: {},
};

export const OutputTabResults: Story = {
args: {
results: [{ name: 'result-1', value: 'value' }],
pipelineRunName: 'pipelineRunName',
pipelineRunStatus: 'Succeeded',
},
parameters: {},
};
32 changes: 0 additions & 32 deletions docs/stories/pipelines/PipelinesButton/PipelinesButton.stories.tsx

This file was deleted.

37 changes: 37 additions & 0 deletions docs/stories/pipelines/ResultList.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import '@patternfly/react-core/dist/styles/base.css';
import { ResultsList } from '@aonic-ui/pipelines/src';
import { acsImageCheckResults } from '@aonic-ui/pipelines/src/components/Output/data';
import type { Meta, StoryObj } from '@storybook/react';

// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
const meta = {
title: 'Pipelines/Output/Cards/Others',
component: ResultsList,
parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
layout: 'padded',
},
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
tags: ['autodocs'],
// More on argTypes: https://storybook.js.org/docs/api/argtypes
argTypes: {},
} satisfies Meta<typeof ResultsList>;
export default meta;

type Story = StoryObj<typeof meta>;

export const OtherTab: Story = {
args: {
results: [
{ name: 'result-key', value: 'results-value' },
{ name: 'result-key-2', value: 'http://www.google.com' },
{
name: 'result-key-3',
value: JSON.stringify(acsImageCheckResults),
},
],
pipelineRunName: 'pipelinerun-name',
pipelineRunStatus: 'Succeeded',
},
parameters: {},
};
2 changes: 1 addition & 1 deletion packages/jest-config/babel.config.cjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module.exports = {
presets: [
"@babel/preset-env",
"@babel/preset-react",
["@babel/preset-react", { runtime: 'automatic' }],
"@babel/preset-typescript",
],
};
13 changes: 12 additions & 1 deletion packages/pipelines/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@
"@aonic-ui/rollup-config": "*",
"@aonic-ui/typescript-config": "*",
"@testing-library/react": "^14.1.2",
"@testing-library/jest-dom": "^6.2.0",
"@patternfly/react-core": "^5.1.2",
"@patternfly/react-icons": "^5.1.2",
"@patternfly/react-table": "^5.1.2",
"@patternfly/react-tokens": "^5.1.2",
"@types/jest": "^29.5.11",
"@types/react": "^18.2.47",
"jest": "^29.7.0",
Expand All @@ -47,7 +52,13 @@
"typescript": "^5.3.3"
},
"peerDependencies": {
"react": "^18.2.0"
"react": "^18.2.0",
"@testing-library/jest-dom": "^6.2.0",
"@patternfly/react-core": "^5.1.2",
"@patternfly/react-icons": "^5.1.2",
"@patternfly/react-table": "^5.1.2",
"@patternfly/react-tokens": "^5.1.2"

},
"publishConfig": {
"access": "public"
Expand Down
80 changes: 80 additions & 0 deletions packages/pipelines/src/components/Output/OutputTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import * as React from 'react';
import OutputTabCard from './OutputTabCard';
import AdvancedClusterSecurity from './Tabs/AdvancedClusterSecurity/AdvancedClusterSecurity';
import EnterpriseContract from './Tabs/EnterpriseContract/EnterpriseContract';
import ResultsList, { ResultsListProps } from './Tabs/Others/ResultsList';
import { ACSCheckResults, ACSImageScanResult, EnterpriseContractPolicy } from './types';
import { getACSCheckIssues } from './utils/acs-image-check-utils';
import { getImageScanIssues } from './utils/acs-image-scan-utils';
import { getEnterpriseContractStatus } from './utils/ec-utils';
import { isEmpty } from './utils/helper-utils';
import { getACStatusLabel, getECStatusLabel } from './utils/summary-utils';

type OutputTabProps = {
enterpriseContractPolicies?: EnterpriseContractPolicy[];
acsImageScanResult?: ACSImageScanResult;
acsImageCheckResults?: ACSCheckResults;
acsDeploymentCheckResults?: ACSCheckResults;
} & ResultsListProps;

const OutputTab: React.FC<OutputTabProps> = ({
enterpriseContractPolicies = [],
acsImageCheckResults = {} as ACSCheckResults,
acsImageScanResult = {} as ACSImageScanResult,
acsDeploymentCheckResults = {} as ACSCheckResults,
results,
pipelineRunName,
pipelineRunStatus,
}) => {
const acsIssuesFound =
getImageScanIssues(acsImageScanResult) ||
getACSCheckIssues(acsImageCheckResults, acsDeploymentCheckResults);

const showECCard = enterpriseContractPolicies?.length > 0;
const showACSCard =
[acsImageScanResult, acsImageCheckResults, acsDeploymentCheckResults].filter((a) => !isEmpty(a))
.length > 0;

const showOnlyResults = !showECCard && !showACSCard;
const ResultsComponent = () => (
<ResultsList
results={results}
pipelineRunName={pipelineRunName}
pipelineRunStatus={pipelineRunStatus}
/>
);
return (
<>
{showECCard && (
<OutputTabCard
title="Enterprise Contract"
badge={getECStatusLabel(getEnterpriseContractStatus(enterpriseContractPolicies))}
isOpen={true}
>
<EnterpriseContract enterpriseContractPolicies={enterpriseContractPolicies} />
</OutputTabCard>
)}
{showACSCard && (
<OutputTabCard
title="Advanced Cluster Security"
badge={getACStatusLabel(acsIssuesFound)}
isOpen={!showECCard}
>
<AdvancedClusterSecurity
acsImageScanResult={acsImageScanResult}
acsImageCheckResults={acsImageCheckResults}
acsDeploymentCheckResults={acsDeploymentCheckResults}
/>
</OutputTabCard>
)}
{results.length > 0 && showOnlyResults ? (
<ResultsComponent data-testid="ec" />
) : results.length > 0 ? (
<OutputTabCard data-testid="results-card" title="Others" isOpen={showOnlyResults}>
<ResultsComponent />
</OutputTabCard>
) : null}
</>
);
};
export default OutputTab;
47 changes: 47 additions & 0 deletions packages/pipelines/src/components/Output/OutputTabCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as React from 'react';
import {
Card,
CardBody,
CardExpandableContent,
CardHeader,
CardTitle,
Flex,
FlexItem,
} from '@patternfly/react-core';

type OutputTabCardProps = {
title: string;
isOpen?: boolean;
badge?: React.ReactNode;
children: React.ReactNode;
};
const OutputTabCard: React.FC<OutputTabCardProps> = ({ title, badge, isOpen, children }) => {
const [tabOpen, setTabOpen] = React.useState<boolean>(isOpen ?? false);
const id = title?.replace(/\//g, '-')?.toLowerCase();

return (
<Card id={id} isExpanded={tabOpen}>
<CardHeader
onExpand={() => setTabOpen((open) => !open)}
isToggleRightAligned={false}
toggleButtonProps={{
id: `${id}-toggle-button`,
'aria-label': title,
'aria-labelledby': `${id}-toggle-button`,
'aria-expanded': tabOpen,
}}
>
<CardTitle id={`{${id}-title}`}>
<Flex gap={{ default: 'gapSm' }}>
<FlexItem data-testid="card-title">{title}</FlexItem>
<FlexItem data-testid="card-badge">{badge}</FlexItem>
</Flex>
</CardTitle>
</CardHeader>
<CardExpandableContent>
<CardBody>{children}</CardBody>
</CardExpandableContent>
</Card>
);
};
export default OutputTabCard;
Loading

0 comments on commit 7bbd9b4

Please sign in to comment.