Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(calendar): sticky options for header & column (VIV-2217) #2156

Merged
merged 14 commits into from
Feb 26, 2025
53 changes: 53 additions & 0 deletions libs/components/src/lib/calendar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,59 @@ Unless provided, choice will be set according to local time preference (e.g. US
<vwc-calendar hour12></vwc-calendar>
```

### Sticky Mode

Set the `sticky-mode` attribute to make the days (`header`) or the time (`column`) or `both` sticky.
If no width or height is set on the calendar there will be no scroll, and no sticky elements.

- Type: `'none'` | `'header'` | `'column'` | `'all'`

```html preview
<div class="wrapper">
<vwc-calendar sticky-mode="all"></vwc-calendar>
</div>

<style>
.wrapper {
display: block;
max-inline-size: 550px;
max-block-size: 550px;
background-color: var(--vvd-color-canvas);
padding: 32px;
}
</style>
```

<vwc-note connotation="warning" icon="warning-line" headline="Setting Sticky-Mode to all">
<p>By default, sticky-mode is set to <code>none</code>. In the next breaking change it will be set to <code>all</code></p>
<p></p>
</vwc-note>

## CSS Variables

### Calendar Column / Header Background Color

When using Sticky-Mode for header or column or both, set the css-variable `--calendar-column-background-color` with background color if its other than `--vvd-color-canvas`

```html preview
<div class="wrapper">
<vwc-calendar sticky-mode="all"></vwc-calendar>
</div>

<style>
.wrapper {
--calendar-header-background-color: var(--vvd-color-neutral-100);
--calendar-column-background-color: var(--vvd-color-neutral-100);

display: block;
max-inline-size: 550px;
max-block-size: 550px;
background-color: var(--vvd-color-neutral-100);
padding: 32px;
}
</style>
```

## Slots

### Day
Expand Down
60 changes: 59 additions & 1 deletion libs/components/src/lib/calendar/calendar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
@use '../../../../shared/src/lib/sass/mixins/border-radius' as
border-radius-variables;

:host {
display: block;
overflow: auto;
block-size: inherit;
max-block-size: inherit;
}

ol {
padding: 0;
margin: 0;
Expand All @@ -16,7 +23,7 @@ ol {
display: grid;
margin: auto;
grid-template-areas:
'. column-headers'
'filler column-headers'
'row-headers calendar';
grid-template-columns: min-content auto;
inline-size: max(100%, 500px);
Expand All @@ -40,6 +47,18 @@ ol {
white-space: nowrap;
}
}

.sticky-column &,
.sticky-all & {
position: sticky;
z-index: 1;
background-color: var(
#{variables.$calendar-column-background-color},
var(#{constants.$vvd-color-canvas})
);
inset-inline-start: 0;
margin-inline-end: 3px;
}
}

.calendar-row {
Expand Down Expand Up @@ -158,4 +177,43 @@ ol {
}
}
}

.sticky-header &,
.sticky-all & {
position: sticky;
z-index: 1;
background-color: var(
#{variables.$calendar-header-background-color},
var(#{constants.$vvd-color-canvas})
);
inset-block-start: 0;
margin-block-end: 3px;
}
}

.filler {
background-color: transparent;
grid-area: filler;

.sticky-header &,
.sticky-all &,
.sticky-column & {
position: sticky;
z-index: 2;
background-color: var(
#{variables.$calendar-header-background-color},
var(#{constants.$vvd-color-canvas})
);
margin-block-end: 3px;
}
.sticky-column & {
inset-inline-start: 0;
}
.sticky-header & {
inset-block-start: 0;
}
.sticky-all & {
inset-block-start: 0;
inset-inline-start: 0;
}
}
14 changes: 14 additions & 0 deletions libs/components/src/lib/calendar/calendar.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe('vwc-calendar', () => {
expect(element.startDay).toBeUndefined();
expect(element.locales).toBeUndefined();
expect(element.hour12).toBeFalsy();
expect(element.stickyMode).toEqual('none');
});

it('should allow being created via createElement', () => {
Expand Down Expand Up @@ -227,6 +228,19 @@ describe('vwc-calendar', () => {
});
});

describe('Stick Mode', () => {
it(`should add class "sticky-*stick-mode*" [role="grid"]]`, async () => {
const stickyMode = 'all';
(element as any).stickyMode = stickyMode;
await elementUpdated(element);

const control = element.shadowRoot?.querySelector(
`.sticky-${stickyMode}`
);
expect(control).toBeInstanceOf(Element);
});
});

describe('focus management', () => {
let grid: HTMLElement;
let shadowRoot: ShadowRoot;
Expand Down
14 changes: 11 additions & 3 deletions libs/components/src/lib/calendar/calendar.template.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { html, repeat } from '@microsoft/fast-element';

import type { Calendar } from './calendar';
import { classNames } from '@microsoft/fast-web-utilities';
import {
getFirstDateOfTheWeek,
getValidDateString,
} from './helpers/calendar.date-functions';

import type { Calendar } from './calendar';

const getClasses = ({ stickyMode }: Calendar) =>
classNames([`sticky-${stickyMode}`, Boolean(stickyMode)]);
/**
* the html days markup
*
Expand Down Expand Up @@ -85,7 +88,12 @@ const ColumnTemplate = html<string>`
`;

export const CalendarTemplate = html<Calendar>`
<div role="grid" @keydown=${(x, c) => x.onKeydown(c.event as KeyboardEvent)}>
<div
role="grid"
class="${getClasses}"
@keydown=${(x, c) => x.onKeydown(c.event as KeyboardEvent)}
>
<div class="filler"></div>
${DaysTemplate}
<div class="calendar-row" role="rowgroup">
${HoursTemplate}
Expand Down
11 changes: 11 additions & 0 deletions libs/components/src/lib/calendar/calendar.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { attr } from '@microsoft/fast-element';
import { VividElement } from '../../shared/foundation/vivid-element/vivid-element';
import { Sticky } from '../enums';
import { CalendarEvent } from './../calendar-event/calendar-event';
import {
ARROW_DOWN,
Expand Down Expand Up @@ -65,6 +66,16 @@ export class Calendar extends VividElement {
*/
@attr({ mode: 'boolean' }) hour12 = false;

/**
* Sticky header and sticky column options
*
* @public
* @remarks
* HTML Attribute: sticky-mode
*/
@attr({ attribute: 'sticky-mode', mode: 'fromView' })
stickyMode: Sticky = Sticky.None;

/**
* @internal
*/
Expand Down
4 changes: 4 additions & 0 deletions libs/components/src/lib/calendar/partials/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ $total-columns: 7;
// the rows according to hours a day
$total-rows: 24;
$gap: 1px;

// variables for the calendar
$calendar-header-background-color: --calendar-header-background-color;
$calendar-column-background-color: --calendar-column-background-color;
15 changes: 15 additions & 0 deletions libs/components/src/lib/calendar/ui.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ test('should show the component', async ({ page }: { page: Page }) => {
const template = `
<vwc-calendar datetime="2022-01-01"></vwc-calendar>
<vwc-calendar datetime="2022-01-01" hour12 locales="he-IL" start-day="sunday" style="direction: rtl"></vwc-calendar>
<div class="wrapper">
<vwc-calendar sticky-mode="all" datetime="2022-01-01"></vwc-calendar>
<style>
.wrapper {
--calendar-header-background-color: var(--vvd-color-neutral-100);
--calendar-column-background-color: var(--vvd-color-neutral-100);

display: block;
max-inline-size: 550px;
max-block-size: 550px;
background-color: var(--vvd-color-neutral-100);
padding: 32px;
}
</style>
</div>
`;

page.setViewportSize({ width: 900, height: 720 });
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions libs/components/src/lib/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,10 @@ export enum MediaSkipBy {
Ten = '10',
Thirty = '30',
}

export enum Sticky {
None = 'none',
Header = 'header',
Column = 'column',
All = 'all',
}
3 changes: 2 additions & 1 deletion libs/wrapper-gen/src/generator/globalTypeDefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
TextAreaResize,
TextFieldType,
} from '@microsoft/fast-foundation';
import { MenuItemRole, MediaSkipBy, TabsGutters } from '@vonage/vivid';
import { MenuItemRole, MediaSkipBy, TabsGutters, Sticky } from '@vonage/vivid';
import { Direction, Orientation } from '@microsoft/fast-web-utilities';
import { TypeUnion } from './types';

Expand All @@ -35,6 +35,7 @@ export const globalTypeDefs: Record<string, TypeUnion> = {
Orientation: typeFromEnumObj(Orientation),
Direction: typeFromEnumObj(Direction),
MediaSkipBy: typeFromEnumObj(MediaSkipBy),
Sticky: typeFromEnumObj(Sticky),

// Types defined by Floating UI:
Placement: [
Expand Down
Loading