Skip to content

Commit

Permalink
feat(datepicker): step 2, styling the component (#207)
Browse files Browse the repository at this point in the history
* feat(datepicker): step 1, component with no style

* feat(datepicker): step 2, styling the component

* feat(datepicker): step 2, styling the component

* feat(datepicker): step 2, styling the component

* feat(datepicker): step 2, styling the component

* feat(datepicker): step 2, styling the component
  • Loading branch information
Leotheluck authored and dpellier committed Oct 4, 2023
1 parent c8664f8 commit cd851a1
Show file tree
Hide file tree
Showing 16 changed files with 519 additions and 68 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming';
import { OdsDatepickerAttribute } from '../interfaces/attributes';

const DEFAULT_ATTRIBUTE: OdsDatepickerAttribute = Object.freeze({
clearable: false,
color: ODS_THEME_COLOR_INTENT.primary,
disabled: false,
error: false,
format: 'dd/mm/yyyy',
inline: false,
placeholder: '',
value: null,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class OdsDatepickerController {
if(!this.component.disabled) {
if (newValue === undefined || newValue === null || isNaN(newValue.getTime())) {
this.component.value = null;
this.component.datepickerInstance?.setDate({ clear: true });
} else {
this.component.value = newValue;
this.component.datepickerInstance?.setDate(newValue);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import type { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming';

interface OdsDatepickerAttribute {
/**
* Defines if the Datepicker should be clearable or not (displays a clear button)
*/
clearable?: boolean;
/**
* Defines the Datepicker's color (see component principles)
*/
color?: ODS_THEME_COLOR_INTENT;
/**
* Defines if the Datepicker should be disabled or not (lower opacity and not interactable)
*/
Expand All @@ -15,6 +21,10 @@ interface OdsDatepickerAttribute {
* Defines which format the Datepicker should be applying (supported formats: https://mymth.github.io/vanillajs-datepicker/#/date-string+format?id=date-format)
*/
format?: string;
/**
* Defines if the Datepicker should be displayed inline or not
*/
inline?: boolean;
/**
* Defines if the Datepicker should display a placeholder message
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { E2EPage } from '@stencil/core/testing';
import type { OdsDatepickerAttribute } from './interfaces/attributes';
import type { OsdsDatepicker } from './osds-datepicker';
import { newE2EPage } from '@stencil/core/testing';
import { odsComponentAttributes2StringAttributes, odsStringAttributes2Str } from '@ovhcloud/ods-common-testing';
import { DEFAULT_ATTRIBUTE } from './constants/default-attributes';
Expand All @@ -15,33 +16,75 @@ describe('e2e:osds-datepicker', () => {
<osds-datepicker ${odsStringAttributes2Str(stringAttributes)}></osds-datepicker>
`);
await page.evaluate(() => document.body.style.setProperty('margin', '0px'));

await page.evaluate(() => {
const pickerElement = document.querySelector('osds-datepicker')?.shadowRoot?.querySelector('.datepicker') as HTMLElement;
pickerElement.style.setProperty('animation', 'none');
});

if (attributes.value) {
const dateStr = attributes.value.toISOString();
await page.evaluate((dateStr) => {
const datepicker = document.querySelector('osds-datepicker') as unknown as OsdsDatepicker;
datepicker.value = new Date(dateStr);
}, dateStr);
}
}

const attributeConfigurations = [
{},
{ format: `mm/dd/yyyy` },
{ error: true },
{ placeholder: `placeholder`, error: true },
{ name: 'clearable', options: [true, false] },
{ name: 'disabled', options: [true, false] },
{ name: 'error', options: [false, true] },
{ name: 'format', options: ['dd/mm/yyyy', 'mm/dd/yyyy'] },
{ name: 'inline', options: [true, false] },
{ name: 'placeholder', options: ['', 'placeholder message'] },
{ name: 'value', options: ['', new Date('1999-11-02')] },
];

function generateCombinations(attributes: typeof attributeConfigurations): Array<Partial<OdsDatepickerAttribute>> {
if (attributes.length === 0) return [{}];

const attribute = attributes[0];
const rest = attributes.slice(1);

const combinationsOfRest = generateCombinations(rest);
return attribute.options.flatMap(option => combinationsOfRest.map(combination => ({
...combination,
[attribute.name]: option
})));
}

const allAttributeCombinations = generateCombinations(attributeConfigurations);

describe('screenshots', () => {
attributeConfigurations.forEach(( attributes ) => {
const attributeDescription = Object.keys(attributes).join(' & ');
[true, false].forEach(displayDatepicker => {
allAttributeCombinations.forEach(attributes => {
const attributeDescription = Object.entries(attributes)
.map(([key, value]) => value ? `${key}=${value}` : '')
.filter(attr => attr)
.join(', ');

it(`${attributeDescription}`, async () => {
await setup({ attributes });
const composedName = [
attributeDescription,
displayDatepicker ? 'datepicker displayed' : ''
].filter(Boolean).join(', ');

await page.waitForChanges();
it(composedName, async () => {
await setup({ attributes });

await page.evaluate(() => {
const element = document.querySelector('osds-datepicker') as HTMLElement;
return { width: element.clientWidth, height: element.clientHeight };
});
await page.waitForChanges();
const datepicker = await page.find('osds-datepicker');
if (displayDatepicker) {
await datepicker.click();
}

await page.setViewport({ width: 600, height:600 });
await page.waitForChanges();

const results = await page.compareScreenshot('datepicker', { fullPage: false, omitBackground: true });
expect(results).toMatchScreenshot({ allowableMismatchedRatio: 0 });
await page.setViewport({ width: 600, height: 600 });

const results = await page.compareScreenshot('datepicker', { fullPage: false, omitBackground: true });
expect(results).toMatchScreenshot({ allowableMismatchedRatio: 0 });
});
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@
// CSS targeted only for this component
// /!\ for theming purposes, it has to be done in theming files
// (i.e. packages/libraries/theming/...)
@keyframes datepicker-appear {
from {
opacity: 0;
}
to {
opacity: 1;
}
}

:host(.osds-datepicker) {
display: flex;
flex-direction: column;
Expand All @@ -21,33 +30,106 @@

:host {
.datepicker {
display: none;
width: 50%;
max-width: clamp(36rem, 50%, 50%);
margin-top: var(--ods-size-04);
height: 0;
position: relative;
display: none;
animation: datepicker-appear 0.2s ease-in-out;

* {
transition: background-color 0.1s ease-in-out;
}

.datepicker-picker {
width: 100%;
height: fit-content;
background: gray;
position: absolute;
border-style: solid;

.datepicker-header,
.datepicker-controls {
display: flex;
justify-content: space-between;

.prev-btn,
.next-btn {
display: flex;
justify-content: center;
align-items: center;
border: none;
cursor: pointer;
}

.view-switch {
display: flex;
justify-content: center;
align-items: center;
border: none;
cursor: pointer;
}
}

.datepicker-main {
.datepicker-view {
.days {
.days-of-week {
display: grid;

.dow {
display: flex;
justify-content: center;
align-items: center;
cursor: default;
}
}

.datepicker-grid {
display: grid;

.datepicker-cell {
display: flex;
justify-content: center;
align-items: center;
border: none;
cursor: pointer;
}

.prev,
.next {
opacity: 50%;
}
}
}
}

.datepicker-grid {
display: grid;

.datepicker-cell {
display: flex;
justify-content: center;
align-items: center;
border: none;
cursor: pointer;
}
}
}
}
}
}

:host([disabled]) {
opacity: .5;
cursor: not-allowed;
}

:host([hasFocus]) {
.datepicker {
display: flex;
}
}

:host([inline]) {
display: inline-block;
}

:host([disabled]) {
.datepicker {
display: none;
}
}

// apply the theme template for the component
@include ods-theme-component() {
@include osds-datepicker-theme-color();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
jest.mock('./core/controller'); // keep jest.mock before any
jest.mock('./core/controller');

import type { SpecPage } from '@stencil/core/testing';
import type { OdsDatepickerAttribute } from './interfaces/attributes';
import { newSpecPage } from '@stencil/core/testing';
import { odsComponentAttributes2StringAttributes, odsStringAttributes2Str, odsUnitTestAttribute } from '@ovhcloud/ods-common-testing';
import { DEFAULT_ATTRIBUTE } from './constants/default-attributes';
import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming';
import { OsdsDatepicker } from './osds-datepicker';
import { OdsDatepickerController } from './core/controller';

Expand All @@ -14,6 +15,8 @@ describe('spec:osds-datepicker', () => {
let instance: OsdsDatepicker;
let controller: OdsDatepickerController;

jest.spyOn(OsdsDatepicker.prototype, 'componentDidLoad').mockImplementation(jest.fn());

afterEach(() => {
jest.clearAllMocks();
});
Expand Down Expand Up @@ -56,6 +59,17 @@ describe('spec:osds-datepicker', () => {
});
});

describe('color', () => {
odsUnitTestAttribute<OdsDatepickerAttribute, 'color'>({
name: 'color',
defaultValue: DEFAULT_ATTRIBUTE.color,
newValue: ODS_THEME_COLOR_INTENT.primary,
value: ODS_THEME_COLOR_INTENT.success,
setup: (value) => setup({ attributes: { ['color']: value } }),
...config,
});
});

describe('disabled', () => {
odsUnitTestAttribute<OdsDatepickerAttribute, 'disabled'>({
name: 'disabled',
Expand Down Expand Up @@ -89,6 +103,17 @@ describe('spec:osds-datepicker', () => {
});
});

describe('inline', () => {
odsUnitTestAttribute<OdsDatepickerAttribute, 'inline'>({
name: 'inline',
defaultValue: DEFAULT_ATTRIBUTE.inline,
newValue: true,
value: false,
setup: (value) => setup({ attributes: { ['inline']: value } }),
...config,
});
});

describe('placeholder', () => {
odsUnitTestAttribute<OdsDatepickerAttribute, 'placeholder'>({
name: 'placeholder',
Expand Down
Loading

0 comments on commit cd851a1

Please sign in to comment.