Skip to content
Open
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
26 changes: 20 additions & 6 deletions web-ui/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
FROM registry.opensource.zalan.do/library/node-18-alpine:latest
FROM registry.opensource.zalan.do/library/node-18-alpine:latest AS builder

MAINTAINER "http://zalando.github.io/"

RUN apk --no-cache add curl

COPY package.json ./
COPY yarn.lock ./
WORKDIR /app

RUN yarn --production
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile

COPY src ./src
COPY server.js ./
COPY webpack ./webpack
COPY .babelrc ./

RUN yarn build

FROM registry.opensource.zalan.do/library/node-18-alpine:latest AS runtime

WORKDIR /app

RUN apk --no-cache add curl

COPY package.json yarn.lock ./
RUN yarn install --production --frozen-lockfile

COPY --from=builder /app/src ./src
COPY --from=builder /app/server.js ./

EXPOSE 3000

Expand Down
19 changes: 17 additions & 2 deletions web-ui/src/client/app/components/__tests__/violations.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,20 @@ describe('Violations component', () => {
const component = shallow(<Violations violations={[]} />);
expect(component.find('Violation')).toHaveLength(0);
});

test('should show MUST and SHOULD counts', () => {
const violationsCount = { must: 2, should: 3 };
const component = shallow(
<Violations violations={[]} violationsCount={violationsCount} />
);
const counts = component.find('.violations-heading__count');

expect(counts).toHaveLength(2);
expect(counts.at(0).text()).toContain('MUST:2');
expect(counts.at(1).text()).toContain('SHOULD:3');
expect(counts.at(0).find('RuleType')).toHaveLength(1);
expect(counts.at(1).find('RuleType')).toHaveLength(1);
});
});

describe('Violation component', () => {
Expand Down Expand Up @@ -168,13 +182,14 @@ describe('ViolationsResult component', () => {
describe('when state is complete with violations', () => {
test('should render violations', () => {
const violations = [{}, {}];
const violationsCount = { must: 1, should: 2 };
const component = shallow(
<ViolationsResult
pending={false}
complete={true}
errorMsgText={null}
violations={violations}
violationsCount={2}
violationsCount={violationsCount}
successMsgTitle=""
successMsgText=""
/>
Expand All @@ -186,7 +201,7 @@ describe('ViolationsResult component', () => {

expect(Violations).toHaveLength(1);
expect(Violations.prop('violations')).toEqual(violations);
expect(Violations.prop('violationsCount')).toEqual(2);
expect(Violations.prop('violationsCount')).toEqual(violationsCount);
});
});
});
38 changes: 33 additions & 5 deletions web-ui/src/client/app/components/violations.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,42 @@ import FluidContainer from './fluid-container.jsx';
import { Link } from 'react-router-dom';

export function Violations(props) {
const {
externalId,
violations = [],
violationsCount = {},
} = props;

const parseCount = value =>
typeof value === 'number' ? value : 0;

const severityCounts = [
{ label: 'MUST', value: parseCount(violationsCount.must) },
{ label: 'SHOULD', value: parseCount(violationsCount.should) },
];

return (
<div>
<div className="dc-row">
<div className="dc-column">
<h3>
VIOLATIONS
<span style={{ float: 'right' }}>
<Link to={'/editor/' + props.externalId} className="dc-link">
<h3 className="violations-heading">
<span className="violations-heading__title">VIOLATIONS</span>
<span className="violations-heading__counts">
{severityCounts.map(({ label, value }) => (
<span
key={label}
className="violations-heading__count"
>
<strong>{label}:</strong>
<span className="violations-heading__count-value">
{value}
</span>
<RuleType type={label} />
</span>
))}
</span>
<span className="violations-heading__link">
<Link to={'/editor/' + externalId} className="dc-link">
<i className="dc-icon dc-icon--interactive dc-icon--link" />
</Link>
</span>
Expand All @@ -25,7 +53,7 @@ export function Violations(props) {
<div className="dc-row">
<div className="dc-column">
<ul className="violations-content">
{props.violations.map((violation, index) => {
{violations.map((violation, index) => {
return <Violation key={index} violation={violation} />;
})}
</ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe('Violations container component', () => {
describe('when call handleFormSubmit', () => {
test('should handle success', () => {
const violations = [{}];
const violationsCount = 1;
const violationsCount = { must: 1, should: 0 };
getApiViolations.mockReturnValueOnce(
Promise.resolve({
violations: violations,
Expand Down
38 changes: 20 additions & 18 deletions web-ui/src/client/app/containers/editor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,28 +135,30 @@ export class Editor extends Violations {
/>
) : null}
<div className="dc-show-from-large">
<ViolationsResult
pending={this.state.pending}
complete={this.state.ajaxComplete}
errorMsgText={this.state.error}
externalId={this.state.externalId}
violations={this.state.violations}
successMsgTitle={this.state.successMsgTitle}
successMsgText={this.state.successMsgText}
/>
<ViolationsResult
pending={this.state.pending}
complete={this.state.ajaxComplete}
errorMsgText={this.state.error}
externalId={this.state.externalId}
violations={this.state.violations}
violationsCount={this.state.violationsCount}
successMsgTitle={this.state.successMsgTitle}
successMsgText={this.state.successMsgText}
/>
</div>
</div>
</div>
<Dialog show={this.state.showOverlay} onHide={this.handleHideOverlay}>
<ViolationsResult
pending={this.state.pending}
complete={this.state.ajaxComplete}
errorMsgText={this.state.error}
externalId={this.state.externalId}
violations={this.state.violations}
successMsgTitle={this.state.successMsgTitle}
successMsgText={this.state.successMsgText}
/>
<ViolationsResult
pending={this.state.pending}
complete={this.state.ajaxComplete}
errorMsgText={this.state.error}
externalId={this.state.externalId}
violations={this.state.violations}
violationsCount={this.state.violationsCount}
successMsgTitle={this.state.successMsgTitle}
successMsgText={this.state.successMsgText}
/>
</Dialog>
</div>
);
Expand Down
1 change: 1 addition & 0 deletions web-ui/src/client/app/containers/url.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export class URL extends Violations {
errorMsgText={this.state.error}
externalId={this.state.externalId}
violations={this.state.violations}
violationsCount={this.state.violationsCount}
successMsgTitle={this.state.successMsgTitle}
successMsgText={this.state.successMsgText}
/>
Expand Down
45 changes: 44 additions & 1 deletion web-ui/src/client/app/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,50 @@ footer {
height: 90%;
}

.violations-heading {
margin: 0 0 1rem;
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: 0.75rem;
}

.violations-heading__counts {
display: flex;
align-items: baseline;
gap: 0.5rem;
font-size: 1rem;
color: #1f1f1f;
}

.violations-heading__count {
display: inline-flex;
align-items: center;
gap: 0.35rem;
font-size: inherit;
color: inherit;
}

.violations-heading__count strong {
font-size: inherit;
font-weight: 600;
color: inherit;
}

.violations-heading__count-value {
font-weight: 500;
}

.violations-heading__count + .violations-heading__count::before {
content: '/';
margin: 0 0.45rem;
color: #d1d1d1;
}

.violations-heading__link {
margin-left: auto;
}

.dc-column {
height: 100%;
}
Expand All @@ -209,4 +253,3 @@ footer {
height: 80%;
}
}