diff --git a/components/calendar/package.json b/components/calendar/package.json index cc4b545ecc..f5c1aadaa7 100644 --- a/components/calendar/package.json +++ b/components/calendar/package.json @@ -29,6 +29,7 @@ "dependencies": { "@vonage/vvd-core": "2.9.0", "lit-element": "^2.4.0", + "lit-html": "^1.3.0", "tslib": "^2.0.3" }, "devDependencies": { diff --git a/components/calendar/src/_vwc-calendar-variables.scss b/components/calendar/src/_vwc-calendar-variables.scss new file mode 100644 index 0000000000..54115a3525 --- /dev/null +++ b/components/calendar/src/_vwc-calendar-variables.scss @@ -0,0 +1,15 @@ +// snap fraction within an hour +$fraction: 4; +// number of columns according to days of week +$total-columns: 7; +// the rows according to hours a day +$total-rows: 24; + + +// event variables +$color: --vvd-calendar-event--primary-color; +$day: --vvd-calendar-event--day; +$start: --vvd-calendar-event--start; +$duration: --vvd-calendar-event--duration; +$indent: --vvd-calendar-event--indent; +$overlap-count: --vvd-calendar-event--overlap-count; diff --git a/components/calendar/src/vwc-calendar-event.scss b/components/calendar/src/vwc-calendar-event.scss index 5a77e791c0..a60bbe8a5c 100644 --- a/components/calendar/src/vwc-calendar-event.scss +++ b/components/calendar/src/vwc-calendar-event.scss @@ -1,22 +1,37 @@ -$indent: --vvd-calendar-event-indent; -$overlap-count: --vvd-calendar-event-overlap-count; -$event-color: --vvd-calendar-event-event-color; -$event-day: --vvd-calendar-event-event-day; -$event-row: --vvd-calendar-event-event-row; +@use '@vonage/vvd-typography/scss/typography'; +@use '@vonage/vvd-design-tokens/build/scss/semantic-variables/scheme-variables'; +@use 'vwc-calendar-variables' as calendar-variables; :host { display: contents; } section { - #{$indent}: calc(var(#{$overlap-count}, 0) * 8px); - z-index: var(#{$overlap-count}); - margin: 1px 1px 1px calc(1px + var(#{$indent})); - background-color: var(#{$event-color}); - border-radius: 3.64752px; - grid-column: var(#{$event-day}); - grid-row: var(#{$event-row}); + #{calendar-variables.$indent}: calc(var(#{calendar-variables.$overlap-count}, 0) * 8px); + z-index: var(#{calendar-variables.$overlap-count}); + overflow: hidden; + padding: 16px; + margin: 1px 1px 1px calc(1px + var(#{calendar-variables.$indent})); + background-color: var(#{calendar-variables.$color}, var(#{scheme-variables.$vvd-color-neutral-40})); + border-radius: 6px; + color: var(#{scheme-variables.$vvd-color-on-primary}); + contain: strict; + grid-column: var(#{calendar-variables.$day}); + grid-row: var(#{calendar-variables.$start}) / span var(#{calendar-variables.$duration}); &:focus { z-index: 2000; } } + +h2 { + @include typography.typography-cat-shorthand('body-2-bold'); + margin: 0 0 4px; + > strong { + font: inherit; + } +} + +p { + @include typography.typography-cat-shorthand('caption'); + margin: 0; +} diff --git a/components/calendar/src/vwc-calendar-event.ts b/components/calendar/src/vwc-calendar-event.ts index 2fa8ddf027..1efe1d7411 100644 --- a/components/calendar/src/vwc-calendar-event.ts +++ b/components/calendar/src/vwc-calendar-event.ts @@ -1,8 +1,9 @@ import '@vonage/vvd-core'; import { - customElement, html, LitElement, property, TemplateResult, unsafeCSS + customElement, html, LitElement, property, TemplateResult } from 'lit-element'; import { style } from './vwc-calendar-event.css'; +import { styleMap } from 'lit-html/directives/style-map'; declare global { interface HTMLElementTagNameMap { @@ -23,31 +24,74 @@ export class VWCCalendarEvent extends LitElement { static styles = [style]; /** - * @prop the label of the event + * @prop the heading of the event * @public * */ - @property({ type: String, reflect: true }) - label?: string; + @property({ type: String, reflect: false }) + heading?: string; + + /** + * @prop the description of the event + * @public + * */ + @property({ type: String, reflect: false }) + description?: string; /** * @prop day - day of the week (starts from 0) * @public * */ - @property({ type: Number, reflect: true }) - day?: 0 | 1 | 2 | 3 | 4 | 5 | 6; + @property({ type: Number, reflect: false }) + day: 1 | 2 | 3 | 4 | 5 | 6 | 7 = 1; + + /** + * @prop color - color of event card + * @public + * */ + @property({ type: String, reflect: false }) + color?: string; + + /** + * @prop sets card display precendence and indentation + * @public + * */ + @property({ type: String, reflect: false, attribute: 'overlap-count' }) + overlapCount?: string; + + /** + * @prop start - time of day event starts + * @public + * */ + @property({ type: Number, reflect: false }) + start = 0; // TODO should be converted to allowed range + + /** + * @prop duration - event's time duration in hours + * @public + * */ + @property({ type: Number, reflect: false }) + duration = 1; // TODO should be converted to allowed range /** * the html markup * @internal * */ protected render(): TemplateResult { + const styles = { + ...this.color && { '--vvd-calendar-event--primary-color': this.color }, + ...this.overlapCount && { '--vvd-calendar-event--overlap-count': this.overlapCount }, + '--vvd-calendar-event--start': (this.start + 1).toString(), + '--vvd-calendar-event--duration': (this.duration).toString(), + '--vvd-calendar-event--day': this.day.toString() + }; return html`
- +

${this.heading}

+

${this.description}

`; } diff --git a/components/calendar/src/vwc-calendar.scss b/components/calendar/src/vwc-calendar.scss index bdbbf38184..947f19fc29 100644 --- a/components/calendar/src/vwc-calendar.scss +++ b/components/calendar/src/vwc-calendar.scss @@ -1,3 +1,4 @@ +@use 'vwc-calendar-variables' as calendar-variables; @use '@vonage/vvd-design-tokens/build/scss/semantic-variables/scheme-variables'; @use '@vonage/vvd-typography/scss/typography'; @@ -7,13 +8,6 @@ ol { list-style: none; } -// snap fraction within an hour -$fraction: 4; -// the rows according to hours a day -$total-rows: 24; -// number of columns according to days of week -$total-columns: 7; - .container { display: grid; width: max(100%, 500px); @@ -27,7 +21,7 @@ $total-columns: 7; .time { display: grid; grid-area: time; - grid-template-rows: repeat($total-rows, 1fr); + grid-template-rows: repeat(calendar-variables.$total-rows, 1fr); margin-inline-end: 15px; > li { display: flex; @@ -46,7 +40,7 @@ $total-columns: 7; .headline { display: grid; grid-area: headline; - grid-template-columns: repeat($total-columns, 1fr); + grid-template-columns: repeat(calendar-variables.$total-columns, 1fr); > li > time { @include typography.typography-cat-shorthand('caption'); text-transform: uppercase; @@ -54,24 +48,22 @@ $total-columns: 7; } .calendar { - $sum: $total-rows * $total-columns; - $next: 1; display: grid; overflow: hidden; - background-color: var(#{scheme-variables.$vvd-color-neutral-20}); - border-radius: 3.64752px; + background-color: var(#{scheme-variables.$vvd-color-neutral-20}, rgb(128, 128, 128)); + border-radius: 6px; box-shadow: 0 0 3.64752px rgba(0, 0, 0, 0.15); counter-reset: listing; gap: 1px; grid-area: calendar; grid-auto-flow: column; - grid-template: repeat($total-rows * $fraction, 1fr) / repeat( - $total-columns, + grid-template: repeat(calendar-variables.$total-rows * calendar-variables.$fraction, 1fr) / repeat( + calendar-variables.$total-columns, 1fr ); > [role="listitem"i] { height: 35px; - background-color: var(#{scheme-variables.$vvd-color-base}); + background-color: var(#{scheme-variables.$vvd-color-base}, rgb(255, 255, 255)); grid-column: var(--column); grid-row: var(--row); &::before { @@ -83,19 +75,23 @@ $total-columns: 7; } } - @for $i from 1 through $total-rows { - $current-row: $i * $fraction - $fraction + 1; - [role="listitem"i]:nth-child(#{$total-rows}n + #{$i}) { - --row: #{$current-row} / span #{$fraction}; + @for $i from 1 through calendar-variables.$total-rows { + $current-row: $i * calendar-variables.$fraction - calendar-variables.$fraction + 1; + [role="listitem"i]:nth-child(#{calendar-variables.$total-rows}n + #{$i}) { + --row: #{$current-row} / span #{calendar-variables.$fraction}; } } - @for $i from 1 through $total-columns { - $sum: $sum - $total-rows + 1; - [role="listitem"i]:nth-child(1n + #{$next}):nth-last-child(1n + #{$sum}) { + + @for $i from 1 through calendar-variables.$total-columns { + /* extra 2 for the sibling events wrapper and the self containment */ + $total-cells: calendar-variables.$total-columns * calendar-variables.$total-rows + 2; + + $from: $i * calendar-variables.$total-rows - calendar-variables.$total-rows + 1; + $less: $total-cells - $i * calendar-variables.$total-rows; + + [role="listitem"i]:nth-child(1n + #{$from}):nth-last-child(1n + #{$less}) { --column: #{$i}; } - /* stylelint-disable-next-line order/order */ - $next: $next + $total-rows; } } diff --git a/components/calendar/src/vwc-calendar.ts b/components/calendar/src/vwc-calendar.ts index f192e6042f..0eb9ad92cf 100644 --- a/components/calendar/src/vwc-calendar.ts +++ b/components/calendar/src/vwc-calendar.ts @@ -51,7 +51,7 @@ export class VWCCalendar extends LitElement { datetime?: Date; #daysLength = 7; - #hoursOfDay = (Array.from({ length: 23 }) as Date[]) + #hours = (Array.from({ length: 23 }) as Date[]) .fill(new Date(new Date().setHours(0, 0, 0))) .map((d, i) => new Date(d.setHours(++i))) @@ -102,7 +102,7 @@ export class VWCCalendar extends LitElement { protected renderTimeCells(): TemplateResult[] { const templates = []; - for (let i = 0; i < (this.#hoursOfDay.length + 1) * this.#daysLength; i++) { + for (let i = 0; i < (this.#hours.length + 1) * this.#daysLength; i++) { templates.push(html`
`); } return templates; @@ -133,7 +133,7 @@ export class VWCCalendar extends LitElement {
    - ${this.#hoursOfDay.map(h => html`
  1. + ${this.#hours.map(h => html`
  2. diff --git a/components/calendar/stories/calendar.stories.js b/components/calendar/stories/calendar.stories.js index 59a9c5e162..4237c5b467 100644 --- a/components/calendar/stories/calendar.stories.js +++ b/components/calendar/stories/calendar.stories.js @@ -11,11 +11,13 @@ export default { }; const Template = args => html` - - - - - + + + + + + + `; export const Basic = Template.bind({}); diff --git a/components/calendar/test/calendar.test.js b/components/calendar/test/calendar.test.js index 9991c96547..5c30788e1b 100644 --- a/components/calendar/test/calendar.test.js +++ b/components/calendar/test/calendar.test.js @@ -28,6 +28,21 @@ describe('calendar', () => { expect(actualElement.shadowRoot.innerHTML).to.equalSnapshot(); }); + it('should set cells in correct day column', async () => { + const [actualElement] = addElement( + textToDomToParent(`<${COMPONENT_NAME}>Button Text`) + ); + await waitNextTask(); + + const { shadowRoot } = actualElement; + const cells = shadowRoot.querySelectorAll('.calendar > [role="listitem"i]'); + + const isCorrectColumns = Array.from(cells).every((cell, i) => getComputedStyle(cell) + .getPropertyValue('--column') == ~~(i / 24) + 1); + + expect(isCorrectColumns).to.equal(true); + }); + describe('API', () => { it('should reflect weekdays as set by property', async () => { const [actualElement] = addElement(