Skip to content

Commit

Permalink
Merge branch 'main' into feat/item-interactions
Browse files Browse the repository at this point in the history
  • Loading branch information
christoph-fricke committed Aug 18, 2022
2 parents 8a0c93a + b70b1af commit 416798d
Show file tree
Hide file tree
Showing 9 changed files with 200 additions and 33 deletions.
29 changes: 29 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Code Checks

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
cache: "npm"
- run: npm ci
- run: npm run lint
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
cache: "npm"
- run: npm ci
- run: npm run test:cov
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,23 @@ After importing the package, the web components can be used in your page markup.
<mutti-timeline lang="de">
<mutti-track>
<mutti-label>Track Name</mutti-label>
<mutti-item start="2020-08-01" end="2021-01-01" scale="0.5"
>Item Content</mutti-item
>
<mutti-item start="2020-08-01" end="2021-01-01" scale="0.5">
Item Content
</mutti-item>
<mutti-item start="2021-08-14" end="2022-03-05">Item Content</mutti-item>
<mutti-item start="2022-04-01" end="2022-04-02">Item Content</mutti-item>
<mutti-static-item
start="2022-01-01"
end="2022-03-02"
aria-label="Vacation"
></mutti-static-item>
</mutti-track>
<mutti-track>
<mutti-label>Track Name</mutti-label>
<mutti-item start="2020-08-01" end="2021-01-01">Item Content</mutti-item>
<mutti-item start="2021-08-14" end="2022-03-05" scale="0.7"
>Item Content</mutti-item
>
<mutti-item start="2021-08-14" end="2022-03-05" scale="0.7">
Item Content
</mutti-item>
<mutti-item start="2022-04-01" end="2022-04-02">Item Content</mutti-item>
</mutti-track>
</mutti-timeline>
Expand Down
60 changes: 35 additions & 25 deletions examples/random.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,27 @@
Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue",
sans-serif;
}
.item {
mutti-static-item {
border-radius: 4px;
background-color: rgba(224, 255, 255, 0.6);
}
mutti-item {
cursor: pointer;
padding: 8px;
border-radius: 4px;
background-color: lightsalmon;
background-color: rgba(255, 160, 122, 0.8);
}
.item:focus {
mutti-item:focus {
outline: none;
background-color: darksalmon;
background-color: rgba(233, 150, 122, 0.8);
}
.label {
mutti-label {
padding: 4px;
width: 100px;
background-color: white;
border-right: 1px solid darkgray;
}
.track {
mutti-track {
border-top: 1px solid darkgray;
border-bottom: 1px solid darkgray;
}
Expand Down Expand Up @@ -62,25 +66,31 @@
${map(
tracks,
(track) => html`
<mutti-track class="track">
<mutti-label class="label">${track.children}</mutti-label>
${map(
track.items,
(item) => html`
<mutti-item
class="item"
start=${item.startDate}
end=${item.endDate}
scale=${item.utilization}
@action=${handleEvent}
@delete=${handleEvent}
>
<span>${item.children}</span>
<small>
${getUtilizationPercentage(item.utilization)}
</small>
</mutti-item>
`
<mutti-track>
<mutti-label>${track.children}</mutti-label>
${map(track.items, (item) =>
item.static
? html`
<mutti-static-item
start=${item.startDate}
end=${item.endDate}
aria-label="Vacation"
></mutti-static-item>
`
: html`
<mutti-item
start=${item.startDate}
end=${item.endDate}
scale=${item.utilization}
@action=${handleEvent}
@delete=${handleEvent}
>
<span>${item.children}</span>
<small>
${getUtilizationPercentage(item.utilization)}
</small>
</mutti-item>
`
)}
</mutti-track>
`
Expand Down
78 changes: 78 additions & 0 deletions src/components/static-item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators.js";
import { cameraProp } from "../controllers/camera-controller.js";
import { MuttiDate } from "../core/date.js";
import { varX } from "../core/properties.js";

/** Custom CSS property names that are related to static items. */
export const staticItemProp = {
length: "--mutti-static-item-length",
nowOffset: "--mutti-static-item-now-offset",
};

const styles = css`
:host {
box-sizing: border-box;
display: inline-block;
overflow: hidden;
white-space: nowrap;
pointer-events: none;
user-select: none;
-webkit-tap-highlight-color: transparent;
-webkit-user-select: none;
position: absolute;
height: 100%;
width: calc(${varX(cameraProp.dayWidth)} * ${varX(staticItemProp.length)});
transform: translateX(
calc(${varX(cameraProp.dayWidth)} * ${varX(staticItemProp.nowOffset)})
);
grid-row: 1 / -1; // Spans the whole track
}
`;

/**
* Static items are special items, which do not participate in collision avoidance
* and are not interactive for the user.
*
* Their default styling render them behind other `mutti-item`s.
*/
@customElement("mutti-static-item")
export class MuttiStaticItemElement extends LitElement {
static override styles = styles;

readonly role = "gridcell";
override slot = "static-item";

@property({ converter: MuttiDate.converter }) start = MuttiDate.now;
@property({ converter: MuttiDate.converter }) end = MuttiDate.from(
this.start,
7
);

protected override willUpdate(changedProperties: PropertyValues): void {
if (changedProperties.has("start") || changedProperties.has("end")) {
this.style.setProperty(
staticItemProp.length,
this.start.getDaysUntil(this.end).toString()
);
}
if (changedProperties.has("start")) {
this.style.setProperty(
staticItemProp.nowOffset,
this.start.getDaysFromNow().toString()
);
}
}

protected override render(): TemplateResult {
return html`<slot></slot>`;
}
}

declare global {
interface HTMLElementTagNameMap {
"mutti-static-item": MuttiStaticItemElement;
}
}
25 changes: 25 additions & 0 deletions src/components/track.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { screen } from "@testing-library/dom";
import { html, render } from "../test-utils/render";
import type { MuttiTrackElement } from "./track";
import "./track";
import "./label";

describe("<mutti-track>", () => {
it("should set its slot property to track", async () => {
Expand All @@ -17,5 +19,28 @@ describe("<mutti-track>", () => {
);
expect(element).toHaveSlot("label");
expect(element).toHaveSlot("item");
expect(element).toHaveSlot("static-item");
});

it("should label itself with a containing <mutti-label>", async () => {
const { element } = await render<MuttiTrackElement>(
html`<mutti-track><mutti-label>Test</mutti-label></mutti-track>`
);

const label = screen.getByText("Test");

expect(label.id).toBe("mutti-label-0");
expect(element.getAttribute("aria-labelledby")).toBe(label.id);
});

it("should preserve the id of its label if exists", async () => {
const { element } = await render<MuttiTrackElement>(
html`<mutti-track><mutti-label id="test">Test</mutti-label></mutti-track>`
);

const label = screen.getByText("Test");

expect(label.id).toBe("test");
expect(element.getAttribute("aria-labelledby")).toBe(label.id);
});
});
19 changes: 18 additions & 1 deletion src/components/track.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { cameraProp } from "../controllers/camera-controller.js";
import { FocusChangeEvent } from "../core/events.js";
import { varX, themeProp } from "../core/properties.js";
import { MuttiItemElement } from "./item.js";
import { MuttiLabelElement } from "./label.js";

/** Custom CSS property names that are related to tracks. */
const trackProp = {
Expand All @@ -21,7 +22,7 @@ const styles = css`
.items-container {
box-sizing: border-box;
display: grid;
grid-template-rows: repeat(${varX(trackProp.subTracks)}, 100px);
grid-template-rows: repeat(${varX(trackProp.subTracks, "1")}, 100px);
align-items: end;
gap: ${varX(themeProp.itemGap, "4px")};
padding: ${varX(themeProp.itemGap, "4px")} 0;
Expand Down Expand Up @@ -53,12 +54,27 @@ export class MuttiTrackElement extends LitElement {
protected override firstUpdated(): void {
const children = Array.from(this.children);

const label = children.find<MuttiLabelElement>(
(c): c is MuttiLabelElement => c instanceof MuttiLabelElement
);
this.connectAriaWithLabel(label);

const items = children.filter(this.isMuttiItem);
this.subTracks = this.orderItemsIntoSubTracks(items);
this.applySubTrackInfoToElements(this.subTracks);
this.fillPositionMap(this.itemPositionMap, this.subTracks);
}

private static trackSequence = 0;
private connectAriaWithLabel(label?: MuttiLabelElement) {
if (!label) return;

label.id = label.id
? label.id
: "mutti-label-" + MuttiTrackElement.trackSequence++;
this.setAttribute("aria-labelledby", label.id);
}

/** Called by the <mutti-timeline> with delegated {@link FocusChangeEvent}s. */
public focusOnRelevantSubTrack(e: FocusChangeEvent): void {
const item = e.target;
Expand Down Expand Up @@ -202,6 +218,7 @@ export class MuttiTrackElement extends LitElement {
return html`
<slot name="label"></slot>
<div class="items-container">
<slot name="static-item"></slot>
<slot name="item"></slot>
</div>
`;
Expand Down
2 changes: 1 addition & 1 deletion src/controllers/camera-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export enum ZoomDetailLevel {
export const cameraProp = {
offset: "--mutti-camera-offset",
zoom: "--mutti-camera-zoom",
dayWidth: "--mutti-dayWidth",
dayWidth: "--mutti-day-width",
};

export class CameraController implements ReactiveController {
Expand Down
1 change: 1 addition & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { MuttiItemElement } from "./components/item.js";
export { MuttiLabelElement } from "./components/label.js";
export { MuttiTimelineElement } from "./components/timeline.js";
export { MuttiStaticItemElement } from "./components/static-item.js";
export { MuttiTrackElement } from "./components/track.js";

export {
Expand Down
2 changes: 2 additions & 0 deletions src/test-utils/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ function oneOf<T>(...args: T[]): T {
////////////////////////////////////////////////////////////////////////////////

export interface Item {
static: boolean;
children: string;
utilization?: number;
startDate: string;
Expand Down Expand Up @@ -66,6 +67,7 @@ export const buildItem = createBuilder<Item>((override) => {
const itemLength = faker.datatype.number({ min: 7, max: 500 });
const startDate = buildDate();
return {
static: faker.datatype.boolean(),
children: faker.commerce.productName(),
startDate: startDate.toString(),
endDate: MuttiDate.from(startDate, itemLength).toString(),
Expand Down

0 comments on commit 416798d

Please sign in to comment.