diff --git a/packages/oui-angular/src/index.js b/packages/oui-angular/src/index.js index 04abcbef..d90dbe87 100644 --- a/packages/oui-angular/src/index.js +++ b/packages/oui-angular/src/index.js @@ -39,6 +39,7 @@ import Switch from "@ovh-ui/oui-switch"; import Tabs from "@ovh-ui/oui-tabs"; import Textarea from "@ovh-ui/oui-textarea"; import Tile from "@ovh-ui/oui-tile"; +import Timepicker from "@ovh-ui/oui-timepicker"; import Tooltip from "@ovh-ui/oui-tooltip"; export default angular @@ -84,6 +85,7 @@ export default angular Tabs, Textarea, Tile, + Timepicker, Tooltip ]) .name; diff --git a/packages/oui-calendar/README.md b/packages/oui-calendar/README.md index 0d54e3cd..29e3a1c2 100644 --- a/packages/oui-calendar/README.md +++ b/packages/oui-calendar/README.md @@ -160,6 +160,16 @@ Use `mode` to set a different selection mode for the calendar * `selectedDates` returns an array of Date objects selected by the user. When there are no dates selected, the array is empty. * `dateStr` returns a string representation of the latest selected Date object by the user. The string is formatted as per the `dateFormat` option. +## Variants + +### Timepicker + +See Action menu component. + +```html:preview + +``` + ## API | Attribute | Type | Binding | One-time Binding | Values | Default | Description @@ -168,19 +178,19 @@ Use `mode` to set a different selection mode for the calendar | `id` | string | @? | yes | n/a | n/a | id attribute of the field | `name` | string | @? | yes | n/a | n/a | name attribute of the field | `placeholder` | string | @? | yes | n/a | n/a | placeholder text -| `inline` | boolean | { - this.options[hook] = (selectedDates, dateStr) => { + this.config[hook] = (selectedDates, dateStr) => { this.model = dateStr; this.$timeout(this[hook]({ selectedDates, dateStr })); }; @@ -28,11 +30,15 @@ export default class { setOptionsProperty (property, value) { if (angular.isDefined(value)) { - this.options[property] = value; + this.config[property] = value; } } initCalendarInstance () { + if (this.options) { + this.config = merge(this.config, this.options); + } + // Set options from attributes this.setOptionsProperty("appendTo", this.appendTo); this.setOptionsProperty("defaultDate", this.model); @@ -50,7 +56,7 @@ export default class { this.setOptionsProperty("dateFormat", this.format); if (angular.isDefined(this.altFormat)) { - this.setOptionsProperty("altInput", true); + this.setOptionsProperty("altInput", !this.disabled); this.setOptionsProperty("altFormat", this.altFormat); } @@ -76,7 +82,7 @@ export default class { }); // Init the flatpickr instance - this.flatpickr = new Flatpickr(this.$element.find("input")[0], this.options); + this.flatpickr = new Flatpickr(this.$element.find("input")[0], this.config); } $onInit () { @@ -84,10 +90,14 @@ export default class { addBooleanParameter(this, "disabled"); addBooleanParameter(this, "enableTime"); addBooleanParameter(this, "inline"); + addBooleanParameter(this, "noCalendar"); addBooleanParameter(this, "required"); addBooleanParameter(this, "static"); addBooleanParameter(this, "weekNumbers"); + addDefaultParameter(this, "id", `ouiCalendar${this.$id}`); + addDefaultParameter(this, "name", `ouiCalendar${this.$id}`); + this.initCalendarInstance(); } @@ -96,16 +106,20 @@ export default class { } $postLink () { - // Avoid $element DOM unsync for jqLite methods this.$timeout(() => { + const controls = angular.element(this.$element[0].querySelectorAll(".oui-calendar__control")); + this.$element .addClass("oui-calendar") .removeAttr("id") .removeAttr("name"); - // Add class for `inline` + // Avoid 'alt-input' to take bad value of placeholder + controls.attr("placeholder", this.placeholder); + if (this.inline) { this.$element.addClass("oui-calendar_inline"); + controls.attr("type", "hidden"); } }); } diff --git a/packages/oui-calendar/src/calendar.html b/packages/oui-calendar/src/calendar.html index a422784f..67b32f10 100644 --- a/packages/oui-calendar/src/calendar.html +++ b/packages/oui-calendar/src/calendar.html @@ -3,20 +3,7 @@ autocomplete="off" ng-attr-id="{{::$ctrl.id}}" ng-attr-name="{{::$ctrl.name}}" - ng-attr-placeholder="{{::$ctrl.placeholder}}" ng-model="$ctrl.model" ng-disabled="$ctrl.disabled" ng-required="$ctrl.required" /> - diff --git a/packages/oui-calendar/src/index.spec.js b/packages/oui-calendar/src/index.spec.js index e35fedf1..d0bf51a4 100644 --- a/packages/oui-calendar/src/index.spec.js +++ b/packages/oui-calendar/src/index.spec.js @@ -65,7 +65,7 @@ describe("ouiCalendar", () => { $timeout.flush(); - expect(controller.options.inline).toBe(true); + expect(controller.config.inline).toBe(true); expect(component.hasClass("oui-calendar_inline")).toBe(true); }); @@ -76,7 +76,7 @@ describe("ouiCalendar", () => { $timeout.flush(); - expect(controller.options.appendTo).toBeUndefined(); + expect(controller.config.appendTo).toBeUndefined(); expect(calendar).toBeNull(); }); @@ -98,6 +98,8 @@ describe("ouiCalendar", () => { const component = testUtils.compileTemplate(''); const input = component.find("input"); + $timeout.flush(); + expect(input.attr("placeholder")).toBe("foo"); }); @@ -106,7 +108,7 @@ describe("ouiCalendar", () => { const ctrl = component.controller("ouiCalendar"); ctrl.setOptionsProperty("foo", "bar"); - expect(ctrl.options.foo).toBe("bar"); + expect(ctrl.config.foo).toBe("bar"); }); it("should change the value formatting of the model and the input", () => { @@ -124,9 +126,9 @@ describe("ouiCalendar", () => { const input = component[0].querySelector(".oui-calendar__control"); const altInput = component[0].querySelector(".oui-calendar__control_alt"); - expect(ctrl.options.dateFormat).toBe(format); - expect(ctrl.options.altInput).toBe(true); - expect(ctrl.options.altFormat).toBe(altFormat); + expect(ctrl.config.dateFormat).toBe(format); + expect(ctrl.config.altInput).toBe(true); + expect(ctrl.config.altFormat).toBe(altFormat); expect(input.value).toBe(formatDate); expect(altInput.value).toBe(altFormatDate); }); @@ -136,7 +138,7 @@ describe("ouiCalendar", () => { const ctrl = component.controller("ouiCalendar"); ctrl.setEventHooks(["foo"]); - expect(typeof ctrl.options.foo).toBe("function"); + expect(typeof ctrl.config.foo).toBe("function"); }); it("should call function of events attributes", () => { @@ -153,7 +155,7 @@ describe("ouiCalendar", () => { onOpenSpy }); const ctrl = component.controller("ouiCalendar"); - const today = ctrl.flatpickr.parseDate("today", ctrl.options.dateFormat); + const today = ctrl.flatpickr.parseDate("today", ctrl.config.dateFormat); ctrl.setModelValue(today); expect(onChangeSpy).toHaveBeenCalledWith([today], ctrl.model); @@ -162,26 +164,5 @@ describe("ouiCalendar", () => { ctrl.flatpickr.close(); expect(onCloseSpy).toHaveBeenCalledWith([today], ctrl.model); }); - - // it("should set the value to today's date when 'today' button is clicked", () => { - // const component = testUtils.compileTemplate(''); - // const ctrl = component.controller("ouiCalendar"); - // const button = component.find("button").eq(0); - // const today = ctrl.flatpickr.formatDate(new Date(), ctrl.options.dateFormat); - - // button.triggerHandler("click"); - // expect(ctrl.model).toBe(today); - // }); - - // it("should reset the value when 'reset' button is clicked", () => { - // const component = testUtils.compileTemplate('', { - // model: "today" - // }); - // const ctrl = component.controller("ouiCalendar"); - // const button = component.find("button").eq(1); - - // button.triggerHandler("click"); - // expect(ctrl.model).toBe(""); - // }); }); }); diff --git a/packages/oui-timepicker/README.md b/packages/oui-timepicker/README.md new file mode 100644 index 00000000..d24d0896 --- /dev/null +++ b/packages/oui-timepicker/README.md @@ -0,0 +1,115 @@ +# Timepicker + + + +## Usage + +### Basic + +```html:preview + +``` + +### Placeholder + +```html:preview + +``` + +### Disabled + +```html:preview + +``` + +### Enabling seconds + +Use `enable-seconds` to show seconds selector. + +```html:preview + +``` + +### Enabling AM/PM + +Use `enable-am-pm` to show AM/PM selector. + +```html:preview + +``` + +### Time formatting + + + See Formatting Tokens for more information. + + +```html:preview + + +
+

Model value: {{$ctrl.formatModel | json}}

+
+``` + +### Inline + +```html:preview +
+ +
+
+ +
+
+ +
+
+ +
+``` + +### Events + + + If you want to access the model inside callbacks, you need to use the modelValue variable as below. + + +```html:preview + + +
+

Model value: {{$ctrl.eventsModel | json}}

+

onChange values: {{$ctrl.onChangeValue | json}}

+

onOpen values: {{$ctrl.onOpenValue | json}}

+

onClose values: {{$ctrl.onCloseValue | json}}

+
+``` + +## API + +| Attribute | Type | Binding | One-time Binding | Values | Default | Description +| ---- | ---- | ---- | ---- | ---- | ---- | ---- +| `model` | object | = | no | See [Supplying Dates](https://flatpickr.js.org/examples/#supplying-dates-for-flatpickr) | n/a | model bound to component +| `id` | string | @? | yes | n/a | n/a | id attribute of the field +| `name` | string | @? | yes | n/a | n/a | name attribute of the field +| `placeholder` | string | @? | yes | n/a | n/a | placeholder text +| `format` | string | @? | yes | See [Formatting Tokens](https://flatpickr.js.org/formatting/) | `H:i` | format the date of the model +| `alt-format` | string | @? | yes | See [Formatting Tokens](https://flatpickr.js.org/formatting/) | `H:i` | format the date of the field. `format` is used if undefined +| `append-to-body` | boolean | { + let $timeout; + let testUtils; + + beforeEach(angular.mock.module("oui.timepicker")); + beforeEach(angular.mock.module("oui.test-utils")); + + beforeEach(inject((_$timeout_, _TestUtils_) => { + $timeout = _$timeout_; + testUtils = _TestUtils_; + })); + + describe("Component", () => { + it("should add the classname .oui-timepicker on the root element", () => { + const component = testUtils.compileTemplate(''); + + $timeout.flush(); + + expect(component.hasClass("oui-timepicker")).toBe(true); + }); + + it("should have an attribute id and name on the input, and removed on the root component", () => { + const component = testUtils.compileTemplate(''); + const input = component.find("input"); + + $timeout.flush(); + + expect(component.attr("id")).toBe(undefined); + expect(input.attr("id")).toBe("foo"); + + expect(component.attr("name")).toBe(undefined); + expect(input.attr("name")).toBe("bar"); + }); + + it("should set the picker inline", () => { + const component = testUtils.compileTemplate(''); + const controller = component.controller("ouiTimepicker"); + + $timeout.flush(); + + expect(controller.inline).toBe(true); + expect(component.hasClass("oui-timepicker_inline")).toBe(true); + }); + + it("should append the picker to the body", () => { + const component = testUtils.compileTemplate(''); + const picker = component[0].querySelector(".flatpickr-calendar"); + + $timeout.flush(); + + expect(picker).toBeNull(); + }); + + it("should have disabled the input", () => { + const component = testUtils.compileTemplate(''); + const input = component.find("input"); + + expect(input.attr("disabled")).toBe("disabled"); + }); + + it("should have required the input", () => { + const component = testUtils.compileTemplate(''); + const input = component.find("input"); + + expect(input.attr("required")).toBe("required"); + }); + + it("should have a placeholder on the input", () => { + const component = testUtils.compileTemplate(''); + const input = component.find("input"); + + $timeout.flush(); + + expect(input.attr("placeholder")).toBe("foo"); + }); + + it("should set 'enableSeconds' to true", () => { + const component = testUtils.compileTemplate(''); + const controller = component.controller("ouiTimepicker"); + + expect(controller.options.enableSeconds).toBe(true); + }); + + it("should set 'time_24hr' to false", () => { + const component = testUtils.compileTemplate(''); + const controller = component.controller("ouiTimepicker"); + + expect(controller.options.time_24hr).toBe(false); + }); + }); +}); diff --git a/packages/oui-timepicker/src/timepicker.component.js b/packages/oui-timepicker/src/timepicker.component.js new file mode 100644 index 00000000..20e37b66 --- /dev/null +++ b/packages/oui-timepicker/src/timepicker.component.js @@ -0,0 +1,29 @@ +import controller from "./timepicker.controller"; +import template from "./timepicker.html"; + +export default { + bindings: { + model: "=", + + id: "@?", + name: "@?", + placeholder: "@?", + format: "@?", + altFormat: "@?", + + appendToBody: " { + this.$element + .addClass("oui-timepicker") + .removeAttr("id") + .removeAttr("name"); + + if (this.inline) { + this.$element.addClass("oui-timepicker_inline"); + } + }); + } +} diff --git a/packages/oui-timepicker/src/timepicker.html b/packages/oui-timepicker/src/timepicker.html new file mode 100644 index 00000000..391f6161 --- /dev/null +++ b/packages/oui-timepicker/src/timepicker.html @@ -0,0 +1,16 @@ + + diff --git a/packages/oui-timepicker/tests/index.js b/packages/oui-timepicker/tests/index.js new file mode 100644 index 00000000..ebd31bdb --- /dev/null +++ b/packages/oui-timepicker/tests/index.js @@ -0,0 +1,7 @@ +import "@ovh-ui/common/test-utils"; + +loadTests(require.context("../src/", true, /.*((\.spec)|(index))$/)); + +function loadTests (context) { + context.keys().forEach(context); +}