diff --git a/app/scripts/modules/core/src/pipeline/executions/execution/execution.less b/app/scripts/modules/core/src/pipeline/executions/execution/execution.less
index 8dcaaaafc9a..7293c6a8fd3 100644
--- a/app/scripts/modules/core/src/pipeline/executions/execution/execution.less
+++ b/app/scripts/modules/core/src/pipeline/executions/execution/execution.less
@@ -1,5 +1,7 @@
@import (reference) '~core/presentation/less/imports/commonImports.less';
+@execution-parameters-icon-width: 1.4em;
+
.execution {
.label {
text-transform: uppercase;
@@ -114,3 +116,17 @@ execution-details-section-nav {
min-width: 50px;
}
}
+
+.execution-parameters-button,
+.execution-details-button {
+ font-size: 0.8em;
+ line-height: 12px;
+
+ span.glyphicon {
+ width: @execution-parameters-icon-width;
+ }
+}
+
+.execution-details-button {
+ margin-top: 6px;
+}
diff --git a/app/scripts/modules/core/src/pipeline/executions/executionGroup/ExecutionGroup.tsx b/app/scripts/modules/core/src/pipeline/executions/executionGroup/ExecutionGroup.tsx
index 6c98943a483..fe3480fc828 100644
--- a/app/scripts/modules/core/src/pipeline/executions/executionGroup/ExecutionGroup.tsx
+++ b/app/scripts/modules/core/src/pipeline/executions/executionGroup/ExecutionGroup.tsx
@@ -240,6 +240,7 @@ export class ExecutionGroup extends React.Component
diff --git a/app/scripts/modules/core/src/pipeline/executions/executions.less b/app/scripts/modules/core/src/pipeline/executions/executions.less
index b139bf6f5c8..a2f527ee483 100644
--- a/app/scripts/modules/core/src/pipeline/executions/executions.less
+++ b/app/scripts/modules/core/src/pipeline/executions/executions.less
@@ -82,11 +82,6 @@
margin-top: 0;
font-weight: 600;
}
- &.group-by-name {
- .execution-bar {
- padding-top: 20px;
- }
- }
}
}
}
diff --git a/app/scripts/modules/core/src/pipeline/status/Artifact.spec.tsx b/app/scripts/modules/core/src/pipeline/status/Artifact.spec.tsx
index 366d55d521c..39975de888b 100644
--- a/app/scripts/modules/core/src/pipeline/status/Artifact.spec.tsx
+++ b/app/scripts/modules/core/src/pipeline/status/Artifact.spec.tsx
@@ -23,14 +23,13 @@ describe('
', () => {
type: ARTIFACT_TYPE,
name: ARTIFACT_NAME,
};
+
component = shallow(
);
- const dl = component.find('dl');
- const dt = dl.find('dt');
- const dd = dl.find('dd');
- expect(dl.length).toEqual(1);
- expect(dt.length).toEqual(1);
- expect(dd.length).toEqual(1);
- expect(dd.at(0).text()).toEqual(ARTIFACT_NAME);
+
+ const artifactName = component.find('.artifact-name');
+
+ expect(artifactName.length).toEqual(1);
+ expect(artifactName.text()).toEqual(ARTIFACT_NAME);
});
it('renders an artifact version if present', function() {
@@ -42,13 +41,11 @@ describe('
', () => {
version,
};
component = shallow(
);
- const dl = component.find('dl');
- const dt = dl.find('dt');
- const dd = dl.find('dd');
- expect(dl.length).toEqual(1);
- expect(dt.length).toEqual(2);
- expect(dd.length).toEqual(2);
- expect(dd.at(1).text()).toEqual(version);
+
+ const artifactVersion = component.find('.artifact-version');
+
+ expect(artifactVersion.length).toEqual(1);
+ expect(artifactVersion.text()).toEqual(` - ${version}`);
});
it('includes the artifact reference in the tootip', function() {
diff --git a/app/scripts/modules/core/src/pipeline/status/Artifact.tsx b/app/scripts/modules/core/src/pipeline/status/Artifact.tsx
index fd68baa8fe9..03861cae498 100644
--- a/app/scripts/modules/core/src/pipeline/status/Artifact.tsx
+++ b/app/scripts/modules/core/src/pipeline/status/Artifact.tsx
@@ -39,7 +39,7 @@ export class Artifact extends React.Component
{
return (
-
+
-
{ArtifactIconService.getPath(type) ? (
@@ -47,14 +47,11 @@ export class Artifact extends React.Component {
{type}
)}
-
- {name}
+
-
+
{name}
+ {version && - {version}
}
+
- {version && (
-
-
- Version
- - {version}
-
- )}
);
diff --git a/app/scripts/modules/core/src/pipeline/status/ExecutionParameters.spec.tsx b/app/scripts/modules/core/src/pipeline/status/ExecutionParameters.spec.tsx
new file mode 100644
index 00000000000..de222762d58
--- /dev/null
+++ b/app/scripts/modules/core/src/pipeline/status/ExecutionParameters.spec.tsx
@@ -0,0 +1,111 @@
+import * as React from 'react';
+import { ShallowWrapper, shallow } from 'enzyme';
+import { mock } from 'angular';
+
+import { REACT_MODULE } from 'core/reactShims';
+
+import { IExecutionParametersProps, ExecutionParameters } from './ExecutionParameters';
+import { IDisplayableParameter } from 'core/pipeline';
+import { IPipeline } from 'core/domain';
+
+describe('', () => {
+ let component: ShallowWrapper;
+
+ beforeEach(mock.module(REACT_MODULE));
+ beforeEach(mock.inject(() => {})); // Angular is lazy.
+
+ it(`show only pin params, but there's no pinnedDisplayableParameters should return null`, function() {
+ const parameters: IDisplayableParameter[] = [{ key: '1', value: 'a' }];
+
+ component = shallow(
+ ,
+ );
+
+ expect(component.get(0)).toEqual(null);
+ });
+
+ it(`show only pinned parameters in 2 columns format`, function() {
+ const parameters: IDisplayableParameter[] = [{ key: '1', value: 'a' }, { key: '2', value: 'b' }];
+
+ component = shallow(
+ ,
+ );
+
+ expect(component.find('.execution-parameters-column').length).toEqual(2);
+ expect(component.find('.parameter-key').length).toEqual(2);
+ expect(component.find('.parameter-value').length).toEqual(2);
+ });
+
+ it(`shows pinned parameters title when all parameters are pinned`, function() {
+ const parameters: IDisplayableParameter[] = [{ key: '1', value: 'a' }, { key: '2', value: 'b' }];
+ const pipelineConfig: IPipeline = {
+ application: 'my-app',
+ id: '123-abc',
+ index: 0,
+ name: 'my-pipeline',
+ stages: [],
+ keepWaitingPipelines: false,
+ limitConcurrent: false,
+ strategy: false,
+ triggers: [],
+ parameterConfig: [],
+ pinAllParameters: true,
+ };
+
+ component = shallow(
+ ,
+ );
+
+ expect(component.find('.execution-parameters-column').length).toEqual(2);
+ expect(component.find('.parameter-key').length).toEqual(2);
+ expect(component.find('.parameter-value').length).toEqual(2);
+ });
+
+ it(`show all params, but there's no displayableParameters should return null`, function() {
+ const parameters: IDisplayableParameter[] = [{ key: '1', value: 'a' }];
+
+ component = shallow(
+ ,
+ );
+
+ expect(component.get(0)).toEqual(null);
+ });
+
+ it(`show all parameters in 2 columns format`, function() {
+ const parameters: IDisplayableParameter[] = [{ key: '1', value: 'a' }, { key: '2', value: 'b' }];
+
+ component = shallow(
+ ,
+ );
+
+ expect(component.find('.params-title').text()).toEqual('Parameters');
+ expect(component.find('.execution-parameters-column').length).toEqual(2);
+ expect(component.find('.parameter-key').length).toEqual(2);
+ expect(component.find('.parameter-value').length).toEqual(2);
+ });
+});
diff --git a/app/scripts/modules/core/src/pipeline/status/ExecutionParameters.tsx b/app/scripts/modules/core/src/pipeline/status/ExecutionParameters.tsx
new file mode 100644
index 00000000000..9b9391ec379
--- /dev/null
+++ b/app/scripts/modules/core/src/pipeline/status/ExecutionParameters.tsx
@@ -0,0 +1,76 @@
+import * as React from 'react';
+
+import { IDisplayableParameter } from 'core/pipeline';
+import { IPipeline } from 'core/domain';
+
+import './executionStatus.less';
+import './executionParameters.less';
+
+export interface IExecutionParametersProps {
+ shouldShowAllParams: boolean;
+ displayableParameters: IDisplayableParameter[];
+ pinnedDisplayableParameters: IDisplayableParameter[];
+ pipelineConfig: IPipeline;
+}
+
+interface IExecutionParametersState {
+ toggle: boolean;
+}
+
+export class ExecutionParameters extends React.Component {
+ constructor(props: IExecutionParametersProps) {
+ super(props);
+ }
+
+ private toggleParameterTruncation(parameter: IDisplayableParameter) {
+ if (parameter.valueTruncated) {
+ return () => {
+ parameter.showTruncatedValue = !parameter.showTruncatedValue;
+ this.setState({ toggle: parameter.showTruncatedValue });
+ };
+ }
+ return null;
+ }
+
+ public render() {
+ const { shouldShowAllParams, displayableParameters, pinnedDisplayableParameters, pipelineConfig } = this.props;
+
+ let parameters = pinnedDisplayableParameters;
+ if (shouldShowAllParams || (pipelineConfig && pipelineConfig.pinAllParameters)) {
+ parameters = displayableParameters;
+ }
+
+ if (!parameters.length) {
+ return null;
+ }
+
+ const halfWay = Math.ceil(parameters.length / 2);
+ const paramsSplitIntoColumns = [parameters.slice(0, halfWay), parameters.slice(halfWay)];
+
+ return (
+
+
{shouldShowAllParams && 'Parameters'}
+
+
+ {paramsSplitIntoColumns.map((c, i) => (
+
+ {c.map(p => (
+
+
{p.key}:
+
+ {p.showTruncatedValue ? p.valueTruncated : p.value}
+ {p.showTruncatedValue ? (
+
+ View Full
+
+ ) : null}
+
+
+ ))}
+
+ ))}
+
+
+ );
+ }
+}
diff --git a/app/scripts/modules/core/src/pipeline/status/ExecutionStatus.spec.tsx b/app/scripts/modules/core/src/pipeline/status/ExecutionStatus.spec.tsx
deleted file mode 100644
index 8676debc022..00000000000
--- a/app/scripts/modules/core/src/pipeline/status/ExecutionStatus.spec.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-import * as React from 'react';
-import { ShallowWrapper, shallow } from 'enzyme';
-import { mock, noop } from 'angular';
-
-import { REACT_MODULE } from 'core/reactShims';
-
-import { IExecution } from 'core/domain';
-
-import { IExecutionStatusProps, IExecutionStatusState, ExecutionStatus } from './ExecutionStatus';
-
-describe('', () => {
- let component: ShallowWrapper;
-
- beforeEach(mock.module(REACT_MODULE));
- beforeEach(mock.inject(() => {})); // Angular is lazy.
-
- function getNewExecutionStatus(execution: IExecution): ShallowWrapper {
- return shallow(
- ,
- );
- }
-
- it('adds parameters, sorted alphabetically, to vm if present on trigger', function() {
- const execution: IExecution = {
- trigger: {
- parameters: {
- a: 'b',
- b: 'c',
- d: 'a',
- },
- },
- } as any;
- component = getNewExecutionStatus(execution);
- expect(component.state().parameters).toEqual([
- { key: 'a', value: '"b"' },
- { key: 'b', value: '"c"' },
- { key: 'd', value: '"a"' },
- ]);
- });
-
- it('does not add parameters to vm if none present in trigger', function() {
- const execution: IExecution = { trigger: {} } as any;
- component = getNewExecutionStatus(execution);
- expect(component.state().parameters).toEqual([]);
- });
-
- it('excludes some parameters if the pipeline is a strategy', function() {
- const execution: IExecution = {
- isStrategy: true,
- trigger: {
- parameters: {
- included: 'a',
- parentPipelineId: 'b',
- strategy: 'c',
- parentStageId: 'd',
- deploymentDetails: 'e',
- cloudProvider: 'f',
- },
- },
- } as any;
- component = getNewExecutionStatus(execution);
- expect(component.state().parameters).toEqual([{ key: 'included', value: '"a"' }]);
- });
-});
diff --git a/app/scripts/modules/core/src/pipeline/status/ExecutionStatus.tsx b/app/scripts/modules/core/src/pipeline/status/ExecutionStatus.tsx
index 9c975d35241..bbae7ab2a21 100644
--- a/app/scripts/modules/core/src/pipeline/status/ExecutionStatus.tsx
+++ b/app/scripts/modules/core/src/pipeline/status/ExecutionStatus.tsx
@@ -1,5 +1,4 @@
import * as React from 'react';
-import * as ReactGA from 'react-ga';
import { has } from 'lodash';
import { IExecution } from 'core/domain';
@@ -10,25 +9,22 @@ import { Registry } from 'core/registry';
import { relativeTime, timestamp } from 'core/utils';
import { ISortFilter } from 'core/filterModel';
import { ExecutionState } from 'core/state';
-import { SETTINGS } from 'core/config/settings';
import { buildDisplayName } from '../executionBuild/buildDisplayName.filter';
import { ExecutionBuildLink } from '../executionBuild/ExecutionBuildLink';
import { ExecutionUserStatus } from './ExecutionUserStatus';
-import { ResolvedArtifactList } from './ResolvedArtifactList';
import './executionStatus.less';
export interface IExecutionStatusProps {
execution: IExecution;
- toggleDetails: (stageIndex?: number) => void;
showingDetails: boolean;
+ showingParams: boolean;
standalone: boolean;
}
export interface IExecutionStatusState {
sortFilter: ISortFilter;
- parameters: Array<{ key: string; value: any }>;
timestamp: string;
}
@@ -38,25 +34,11 @@ export class ExecutionStatus extends React.Component = [];
-
const { execution } = this.props;
- if (execution.trigger && execution.trigger.parameters) {
- parameters = Object.keys(execution.trigger.parameters)
- .sort()
- .filter(paramKey => (execution.isStrategy ? !strategyExclusions.includes(paramKey) : true))
- .map((paramKey: string) => {
- return { key: paramKey, value: JSON.stringify(execution.trigger.parameters[paramKey]) };
- });
- }
this.state = {
sortFilter: ExecutionState.filterModel.asFilterModel.sortFilter,
- parameters,
- timestamp: relativeTime(this.props.execution.startTime),
+ timestamp: relativeTime(execution.startTime),
};
}
@@ -100,15 +82,9 @@ export class ExecutionStatus extends React.Component {
- ReactGA.event({ category: 'Pipeline', action: 'Execution details toggled (Details link)' });
- this.props.toggleDetails();
- };
-
public render() {
- const { execution, showingDetails, standalone } = this.props;
+ const { execution } = this.props;
const { trigger, authentication } = execution;
- const { artifacts, resolvedExpectedArtifacts } = trigger;
const TriggerExecutionStatus = this.getTriggerExecutionStatus();
return (
@@ -129,23 +105,7 @@ export class ExecutionStatus extends React.Component
- {this.state.parameters.map(p => (
- -
- {p.key}: {p.value}
-
- ))}
- {SETTINGS.feature.artifacts && (
-
- )}
- {!standalone && (
-
-
- Details
-
- )}
);
}
diff --git a/app/scripts/modules/core/src/pipeline/status/ResolvedArtifactList.spec.tsx b/app/scripts/modules/core/src/pipeline/status/ResolvedArtifactList.spec.tsx
index ea149bba97e..61c867e436a 100644
--- a/app/scripts/modules/core/src/pipeline/status/ResolvedArtifactList.spec.tsx
+++ b/app/scripts/modules/core/src/pipeline/status/ResolvedArtifactList.spec.tsx
@@ -19,7 +19,7 @@ describe('', () => {
it('renders null when null artifacts are passed in', function() {
const artifacts: IArtifact[] = null;
- component = shallow();
+ component = shallow();
expect(component.get(0)).toEqual(null);
});
@@ -27,12 +27,16 @@ describe('', () => {
const artifacts: IArtifact[] = [];
const resolvedExpectedArtifacts = artifacts.map(a => ({ boundArtifact: a } as IExpectedArtifact));
component = shallow(
- ,
+ ,
);
expect(component.get(0)).toEqual(null);
});
- it('renders a list when artifacts are passed in', function() {
+ it('renders null when artifacts are set to not expanded', () => {
const artifacts: IArtifact[] = [
{
id: 'abcd',
@@ -42,9 +46,40 @@ describe('', () => {
];
const resolvedExpectedArtifacts = artifacts.map(a => ({ boundArtifact: a } as IExpectedArtifact));
component = shallow(
- ,
+ ,
);
- expect(component.find(Artifact).length).toEqual(1);
+ expect(component.get(0)).toEqual(null);
+ });
+
+ it('renders two columns when columnLayoutAfter is set to 2', function() {
+ const artifacts: IArtifact[] = [
+ {
+ id: 'abcd',
+ type: ARTIFACT_TYPE,
+ name: ARTIFACT_NAME,
+ },
+ {
+ id: 'efgh',
+ type: ARTIFACT_TYPE,
+ name: ARTIFACT_NAME,
+ },
+ ];
+
+ const resolvedExpectedArtifacts = artifacts.map(a => ({ boundArtifact: a } as IExpectedArtifact));
+ component = shallow(
+ ,
+ );
+
+ expect(component.find('.artifact-list-column').length).toEqual(2);
+ expect(component.find(Artifact).length).toEqual(2);
});
it('does not render an artifact without a type and name', function() {
@@ -55,12 +90,16 @@ describe('', () => {
];
const resolvedExpectedArtifacts = singleArtifact.map(a => ({ boundArtifact: a } as IExpectedArtifact));
component = shallow(
- ,
+ ,
);
expect(component.get(0)).toEqual(null);
});
- it('renders an artifacts that does have a type and name', function() {
+ it('only renders an artifacts that has a type and name', function() {
const artifacts: IArtifact[] = [
{
id: 'abcd',
@@ -73,7 +112,11 @@ describe('', () => {
];
const resolvedExpectedArtifacts = artifacts.map(a => ({ boundArtifact: a } as IExpectedArtifact));
component = shallow(
- ,
+ ,
);
expect(component.find(Artifact).length).toEqual(1);
});
@@ -86,8 +129,8 @@ describe('', () => {
name: ARTIFACT_NAME,
},
];
- component = shallow();
- const li = component.find('li');
+ component = shallow();
+ const li = component.find('.extraneous-artifacts');
expect(li.text()).toMatch(/1.*artifact.*not.*consumed/);
});
});
diff --git a/app/scripts/modules/core/src/pipeline/status/ResolvedArtifactList.tsx b/app/scripts/modules/core/src/pipeline/status/ResolvedArtifactList.tsx
index 3e21693191d..112a894d205 100644
--- a/app/scripts/modules/core/src/pipeline/status/ResolvedArtifactList.tsx
+++ b/app/scripts/modules/core/src/pipeline/status/ResolvedArtifactList.tsx
@@ -8,11 +8,16 @@ import './artifactList.less';
export interface IResolvedArtifactListProps {
artifacts: IArtifact[];
resolvedExpectedArtifacts?: IExpectedArtifact[];
+ showingExpandedArtifacts: boolean;
}
export class ResolvedArtifactList extends React.Component {
+ constructor(props: IResolvedArtifactListProps) {
+ super(props);
+ }
+
public render() {
- let { artifacts, resolvedExpectedArtifacts } = this.props;
+ let { artifacts, resolvedExpectedArtifacts, showingExpandedArtifacts } = this.props;
artifacts = artifacts || [];
resolvedExpectedArtifacts = resolvedExpectedArtifacts || [];
@@ -29,28 +34,44 @@ export class ResolvedArtifactList extends React.Component rea.boundArtifact)
.filter(({ name, type }) => name && type);
- if (decoratedArtifacts.length === 0 && decoratedExpectedArtifacts.length === 0) {
+ // if there's none, don't show it
+ if (!showingExpandedArtifacts || (decoratedArtifacts.length === 0 && decoratedExpectedArtifacts.length === 0)) {
+ return null;
+ }
+
+ // if we're exceeding the limit, don't show it
+ if (!showingExpandedArtifacts) {
return null;
}
+ const halfIndex = Math.ceil(decoratedArtifacts.length / 2);
+ const columns = [decoratedArtifacts.slice(0, halfIndex), decoratedArtifacts.slice(halfIndex)];
+
return (
-