Skip to content
Draft
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
5 changes: 5 additions & 0 deletions packages/data-access/src/mappers/DashboardQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ export class DashboardQuery<TModel, TPartitionKeyFields extends keyof TModel, TR
return new DashboardQuery(this.ModelClass, [...this.whereConditions, whereCondition]);
}

public findPartitionKeyRange(start: string): DashboardQuery<TModel, TPartitionKeyFields, TRowKeyFields> {
const whereCondition: string = odata`PartitionKey ge ${start}`;
return new DashboardQuery(this.ModelClass, [...this.whereConditions, whereCondition]);
}

public static create<TModel, TPartitionKeyFields extends keyof TModel, TRowKeyFields extends keyof TModel>(
ModelClass: ModelClass<TModel, TPartitionKeyFields, TRowKeyFields>,
): DashboardQuery<TModel, TPartitionKeyFields, TRowKeyFields> {
Expand Down
21 changes: 19 additions & 2 deletions packages/stryker-elements/src/lib/atoms/dropdown.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { html } from 'lit';
import { html, nothing } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { map } from 'lit/directives/map.js';
import { when } from 'lit/directives/when.js';

import { BaseElement } from '../base-element';

Expand All @@ -12,14 +13,30 @@ export class Dropdown extends BaseElement {
@property({ type: Array })
options: { name: string; value: string }[] = [];

@property({ type: String })
selectedOption = '';

@property({ type: Boolean })
withDisabledEmtpyOption = false;

render() {
return html`
<select
id="${this.id}"
class="w-full rounded-lg bg-neutral-800 p-2 text-3xl text-white"
@change="${this.#handleChange}"
>
${map(this.options, (option) => html`<option value="${option.value}">${option.name}</option>`)}
${when(
this.withDisabledEmtpyOption,
() => html`<option value="" disabled selected>Select an option</option>`,
() => nothing,
)}
${map(
this.options,
(option) => html`
<option value="${option.value}" ?selected="${option.value === this.selectedOption}">${option.name}</option>
`,
)}
</select>
`;
}
Expand Down
17 changes: 15 additions & 2 deletions packages/stryker-elements/src/lib/atoms/hr.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
import { html } from 'lit';
import { customElement } from 'lit/decorators.js';
import { customElement, property } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';

import { BaseElement } from '../base-element';

@customElement('sme-hr')
export class Hr extends BaseElement {
@property()
direction: 'horizontal' | 'vertical' = 'horizontal';

@property()
color: 'normal' | 'bright' = 'normal';

render() {
return html`<hr class="my-2 rounded border-t-[3px] border-neutral-700" />`;
const color = classMap({ 'bg-neutral-700': this.color === 'normal', 'bg-neutral-400': this.color === 'bright' });

if (this.direction === 'vertical') {
return html`<hr class="${color} h-full w-[3px] rounded" />`;
}

return html`<hr class="${color} my-2 rounded border-t-[3px]" />`;
}
}

Expand Down
13 changes: 12 additions & 1 deletion packages/stryker-elements/src/lib/molecules/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export class Loader extends BaseElement {
@property({ type: Boolean, reflect: true })
useSpinner = false;

@property({ type: Boolean, reflect: true })
useFullHeight = false;

@property({ type: Boolean, reflect: true })
loading = true;

Expand All @@ -30,6 +33,10 @@ export class Loader extends BaseElement {
this.requestUpdate();
this.#showSpinner = true;
}, this.#showSpinnerDelay);

// if (this.useFullHeight) {
this.style.height = '100%';
// }
}

updated(changedProperties: Map<string | number | symbol, unknown>) {
Expand All @@ -54,9 +61,13 @@ export class Loader extends BaseElement {
'pointer-events-none': this.loading,
});

const fullHeightClassMap = classMap({
'h-full': this.useFullHeight,
});

return this.useSpinner
? html`
<div class="relative flex justify-center">
<div class="${fullHeightClassMap} relative flex justify-center">
<div class="${contentClassMap} w-full transition duration-300">
<slot></slot>
</div>
Expand Down
30 changes: 30 additions & 0 deletions packages/stryker-elements/src/lib/molecules/split-layout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { html } from 'lit';
import { customElement, property } from 'lit/decorators.js';

import { BaseElement } from '../base-element.js';

@customElement('sme-split-layout')
export class SplitLayout extends BaseElement {
@property({ type: Boolean })
withBackground = false;

render() {
return html`
<div class="flex p-4">
<div class="m-4 max-w-split">
<slot name="left"></slot>
</div>
<sme-hr class="ml-2 mr-2" color="bright" direction="vertical"></sme-hr>
<div class="m-4 max-w-split">
<slot name="right"></slot>
</div>
</div>
`;
}
}

declare global {
interface HTMLElementTagNameMap {
'sme-split-layout': SplitLayout;
}
}
66 changes: 66 additions & 0 deletions packages/stryker-elements/src/lib/molecules/tab-panels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import type { TemplateResult } from 'lit';
import { html } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { map } from 'lit/directives/map.js';

import { BaseElement } from '../base-element';

@customElement('sme-tab-panels')
export class TabPanels extends BaseElement {
@property({ type: Array })
tabs: string[] = [];

@property({ type: Array })
panels: TemplateResult<1>[] = [];

@state()
activeTab = 0;

protected firstUpdated(): void {
this.style.display = 'block';
this.style.height = '100%';
}

render() {
return html`
<div class="w-full">
<div class="flex justify-center bg-red-900">
${map(this.tabs, (tab, index) => {
return html`
<sme-button
@click="${this.#handleTabChange}"
class="${classMap({
'bg-red-700': index === this.activeTab,
'border-red-500': index === this.activeTab, // separate on multiple lines because it cannot contain whitespace
'border-transparent': index !== this.activeTab,
})} text-md border-0 border-b-2 px-2 font-bold text-white transition"
type="plain"
>${tab}</sme-button
>
`;
})}
</div>
<div class="h-full w-full">
${map(this.panels, (panel, index) => {
return html`
<div id="panel-${index}" class="${classMap({ hidden: index !== this.activeTab })}">${panel}</div>
`;
})}
</div>
</div>
`;
}

#handleTabChange(e: Event) {
const target = e.target as HTMLElement;
const index = this.tabs.indexOf(target.innerText);
this.activeTab = index;
}
}

declare global {
interface HTMLElementTagNameMap {
'sme-tab-panels': TabPanels;
}
}
3 changes: 2 additions & 1 deletion packages/stryker-elements/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ export { Loader } from './lib/molecules/loader';
export { Modal } from './lib/molecules/modal';
export { ProfileButton } from './lib/molecules/profile-button';
export { Repository } from './lib/molecules/repository';
export { SupportedFrameworkList } from './lib/molecules/supported-framework-list';
export { SplitLayout } from './lib/molecules/split-layout';
export { TabPanels } from './lib/molecules/tab-panels';
export { ToggleRepository } from './lib/molecules/toggle-repository';

// Organisms
Expand Down
18 changes: 18 additions & 0 deletions packages/stryker-elements/src/stories/atoms/hr.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import '../../lib/atoms/hr';

import type { Meta, StoryObj } from '@storybook/web-components';
import { html } from 'lit';

export default {
title: 'Atoms/Hr',
} as Meta;

export const Default: StoryObj = {
name: 'Default',
render: () => html`<sme-hr></sme-hr>`,
};

export const Vertical: StoryObj = {
name: 'Vertical',
render: () => html`<div style="height: 100px"><sme-hr direction="vertical"></sme-hr></div>`,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import '../../lib/atoms/hr';
import '../../lib/molecules/split-layout';

import type { Meta, StoryObj } from '@storybook/web-components';
import { html } from 'lit';

export default {
title: 'Molecules/Split Layout',
component: 'sme-split-layout',
} as Meta;

export const Default: StoryObj = {
name: 'Default',
render: () => {
return html`
<sme-split-layout>
<div slot="left">Left</div>
<div slot="right">Right</div>
</sme-split-layout>
`;
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import '../../lib/molecules/tab-panels';

import type { Meta, StoryObj } from '@storybook/web-components';
import { html } from 'lit';

export default {
title: 'Molecules/Tab Panels',
component: 'sme-tab-panels',
} as Meta;

export const Default: StoryObj = {
name: 'Default',
render: () => {
return html`<sme-tab-panels
.tabs="${['Report', 'Compare']}"
.panels="${[html`<div>report</div>`, html`<div>compare</div>`]}"
></sme-tab-panels>`;
},
};
4 changes: 4 additions & 0 deletions packages/stryker-elements/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export default {
colors: {
background: '#242526',
hero_background: '#22323D',
elementsDark: '#18181b',
},
spacing: {
split: 'calc(50% - 9.5px)',
},
keyframes: {
something: {
Expand Down
25 changes: 24 additions & 1 deletion packages/website-backend/src/controllers/version.controller.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
import { Controller, Get } from '@nestjs/common';
import { Controller, Get, Param } from '@nestjs/common';
import {
createMutationTestingReportMapper,
DashboardQuery,
MutationTestingReport,
MutationTestingReportMapper,
} from '@stryker-mutator/dashboard-data-access';
import { version as frontendVersion } from '@stryker-mutator/dashboard-frontend';
import fs from 'fs/promises';

import { parseSlug } from '../utils/utils.js';

const dashboardVersion = (
JSON.parse(await fs.readFile(new URL('../../../package.json', import.meta.url), 'utf-8')) as { version: string }
).version;

@Controller('/version')
export default class VersionController {
#reportMapper: MutationTestingReportMapper;

constructor() {
this.#reportMapper = createMutationTestingReportMapper();
}

/**
* Gets the current version of the dashboard
*/
Expand All @@ -18,4 +32,13 @@ export default class VersionController {
frontend: frontendVersion,
};
}

@Get('/:slug(*)')
public async getVersionsForReport(@Param('slug') slug: string): Promise<string[]> {
const { project } = parseSlug(slug);
const reports = await this.#reportMapper.findAll(
DashboardQuery.create(MutationTestingReport).findPartitionKeyRange(project),
);
return reports.map((project) => project.model.version);
}
}
Loading
Loading