-
-
-
{{ item.label }}
+
+ {{ label }}
+
+
+
+
+
+
+
{{ item.label }}
+
+ {{ error }}
+
diff --git a/src/components/form/HdSelect.vue b/src/components/form/HdSelect.vue
new file mode 100644
index 000000000..2266e3214
--- /dev/null
+++ b/src/components/form/HdSelect.vue
@@ -0,0 +1,165 @@
+
+
+
+ {{ option.label }}
+
+
{{ label }}
+
{{ error }}
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form/HdTagsSelector.vue b/src/components/form/HdTagsSelector.vue
new file mode 100644
index 000000000..74a2cbce8
--- /dev/null
+++ b/src/components/form/HdTagsSelector.vue
@@ -0,0 +1,240 @@
+
+
+
+
+
+
+
diff --git a/src/components/form/HdTextarea.vue b/src/components/form/HdTextarea.vue
new file mode 100644
index 000000000..061e08b53
--- /dev/null
+++ b/src/components/form/HdTextarea.vue
@@ -0,0 +1,205 @@
+
+
+
+
+ {{ label }}
+
+
+ {{ error }}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/gallery/HdGallery.vue b/src/components/gallery/HdGallery.vue
new file mode 100644
index 000000000..4cb1bef46
--- /dev/null
+++ b/src/components/gallery/HdGallery.vue
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/gallery/HdGalleryCarousel.vue b/src/components/gallery/HdGalleryCarousel.vue
new file mode 100644
index 000000000..9c29ddb21
--- /dev/null
+++ b/src/components/gallery/HdGalleryCarousel.vue
@@ -0,0 +1,407 @@
+
+
+
+
+
+
+
diff --git a/src/components/gallery/HdGalleryMedia.vue b/src/components/gallery/HdGalleryMedia.vue
new file mode 100644
index 000000000..024ae0e26
--- /dev/null
+++ b/src/components/gallery/HdGalleryMedia.vue
@@ -0,0 +1,217 @@
+
+
+
+
+
+ {{ itemCaption || item.caption }}
+
+
+
+
+
+
+
+
diff --git a/src/components/gallery/HdGalleryPlaceholder.vue b/src/components/gallery/HdGalleryPlaceholder.vue
new file mode 100644
index 000000000..2b235fca3
--- /dev/null
+++ b/src/components/gallery/HdGalleryPlaceholder.vue
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+ {{ text }}
+
+
+
+
+
+
+
+
diff --git a/src/components/notifications/HdNotifications.vue b/src/components/notifications/HdNotifications.vue
new file mode 100644
index 000000000..a04b4d776
--- /dev/null
+++ b/src/components/notifications/HdNotifications.vue
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
diff --git a/src/components/notifications/HdNotificationsBar.vue b/src/components/notifications/HdNotificationsBar.vue
new file mode 100644
index 000000000..d364061e8
--- /dev/null
+++ b/src/components/notifications/HdNotificationsBar.vue
@@ -0,0 +1,174 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lang/de.json b/src/lang/de.json
index c6e553d10..5e6278cd8 100644
--- a/src/lang/de.json
+++ b/src/lang/de.json
@@ -1,4 +1,10 @@
{
+ "GENERAL": {
+ "EDIT": "Bearbeiten",
+ "SAVE": "Speichern",
+ "CANCEL": "Abbrechen",
+ "SAVING": "Speichern"
+ },
"FORM": {
"EMAIL": {
"PLACEHOLDER": "z.b. michael.schmidt@gmail.com",
@@ -11,6 +17,7 @@
"VALIDATION": {
"REQUIRED": "Dies ist ein Pflichtfeld.",
"INVALID_EMAIL": "Bitte geben Sie eine korrekte E-Mail-Adresse ein.",
+ "INVALID_DATE": "Bitte geben Sie ein Datum in diesem Format ein: JJJJ-MM-TT",
"PASSWORD_MISMATCH": "Dies stimmt nicht mit dem obigen Passwort überein",
"PASSWORD_SHORT": "Dieses Passwort ist zu kurz",
"PASSWORD_STRENGTH": {
@@ -23,5 +30,11 @@
]
}
}
+ },
+ "EXPAND_TEXT": {
+ "TOGGLE": {
+ "SHOW_MORE": "weiterlesen",
+ "SHOW_LESS": "zuklappen"
+ }
}
}
diff --git a/src/lang/en.json b/src/lang/en.json
index f0f4224ee..df7f47008 100644
--- a/src/lang/en.json
+++ b/src/lang/en.json
@@ -1,4 +1,10 @@
{
+ "GENERAL": {
+ "EDIT": "Edit",
+ "SAVE": "Save",
+ "CANCEL": "Cancel",
+ "SAVING": "Saving"
+ },
"FORM": {
"EMAIL": {
"PLACEHOLDER": "e.g. will.smith@gmail.com",
@@ -11,6 +17,7 @@
"VALIDATION": {
"REQUIRED": "This field is required",
"INVALID_EMAIL": "Please enter a valid email address",
+ "INVALID_DATE": "Please enter a date in this format: YYYY-MM-DD",
"PASSWORD_MISMATCH": "The entered passwords are not matching",
"PASSWORD_SHORT": "This password is too short",
"PASSWORD_STRENGTH": {
@@ -23,5 +30,11 @@
]
}
}
+ },
+ "EXPAND_TEXT": {
+ "TOGGLE": {
+ "SHOW_MORE": "show more",
+ "SHOW_LESS": "show less"
+ }
}
}
diff --git a/src/notes/HdNotifications.md b/src/notes/HdNotifications.md
new file mode 100644
index 000000000..a7293ed72
--- /dev/null
+++ b/src/notes/HdNotifications.md
@@ -0,0 +1,57 @@
+Sample Vuex store module:
+
+```js
+import uuidv4 from 'uuid/v4';
+import Vue from 'vue';
+
+const NOTIFICATION_DEFAULTS = {
+ type: 'notification',
+ message: '',
+ duration: 5000,
+ customIcon: '',
+};
+
+export default {
+ state: {
+ notifications: [],
+ },
+ actions: {
+ addNotification({ commit, dispatch, state }, {
+ type = NOTIFICATION_DEFAULTS.type,
+ message = NOTIFICATION_DEFAULTS.message,
+ customIcon = NOTIFICATION_DEFAULTS.customIcon,
+ // For how long should the notification be displayed before it hides itself
+ // Set to 0 for a permanent notification
+ duration = NOTIFICATION_DEFAULTS.duration,
+ }) {
+ const notification = {
+ type,
+ message,
+ customIcon,
+ id: uuidv4(),
+ };
+
+ commit('setNotifications', [...state.notifications, notification]);
+
+ if (duration > 0) {
+ setTimeout(() => {
+ dispatch('removeNotification', {
+ id: notification.id,
+ });
+ }, duration);
+ }
+
+ return notification.id;
+ },
+ removeNotification({ commit, state }, { id: idToRemove }) {
+ const filteredNotifications = state.notifications.filter(({ id }) => id !== idToRemove);
+ commit('setNotifications', filteredNotifications);
+ },
+ },
+ mutations: {
+ setNotifications(state, notifications) {
+ Vue.set(state, 'notifications', notifications);
+ },
+ },
+};
+```
\ No newline at end of file
diff --git a/src/notes/HdRadioButton.md b/src/notes/HdRadioButton.md
deleted file mode 100644
index b615d65e7..000000000
--- a/src/notes/HdRadioButton.md
+++ /dev/null
@@ -1,2 +0,0 @@
-## Radio Button
-State of art Radio Button for all you important choices
\ No newline at end of file
diff --git a/src/services/breakpoints.js b/src/services/breakpoints.js
new file mode 100644
index 000000000..f8511c0a1
--- /dev/null
+++ b/src/services/breakpoints.js
@@ -0,0 +1,21 @@
+const BREAKPOINT_TABLET = '(min-width:768px)';
+const BREAKPOINT_DESKTOP = '(min-width:1280px)';
+
+let breakpoints = {
+ tablet: BREAKPOINT_TABLET,
+ desktop: BREAKPOINT_DESKTOP,
+};
+
+export const isMobile = () => !window.matchMedia(breakpoints.tablet).matches;
+export const isDesktop = () => window.matchMedia(breakpoints.desktop).matches;
+export const isTablet = () => !isDesktop() && window.matchMedia(breakpoints.tablet).matches;
+export const setBreakpoints = (newBreakpoints) => {
+ breakpoints = { ...breakpoints, ...newBreakpoints };
+};
+
+export default {
+ isMobile,
+ isTablet,
+ isDesktop,
+ setBreakpoints,
+};
diff --git a/src/services/event-emitter.js b/src/services/event-emitter.js
new file mode 100644
index 000000000..f94b08a61
--- /dev/null
+++ b/src/services/event-emitter.js
@@ -0,0 +1,5 @@
+import Vue from 'vue';
+
+const EventEmitter = new Vue();
+
+export default EventEmitter;
diff --git a/src/services/flickity.js b/src/services/flickity.js
new file mode 100644
index 000000000..268caa954
--- /dev/null
+++ b/src/services/flickity.js
@@ -0,0 +1,16 @@
+export function hidePaginationWhenNotNeeded() {
+ const viewport = this.size.width;
+ const fullSize = Math.round(this.slideableWidth);
+
+ if (fullSize > viewport) {
+ this.bindDrag();
+ this.element.classList.remove('flickity--no-controls');
+ } else if (fullSize <= viewport) {
+ this.unbindDrag();
+ this.element.classList.add('flickity--no-controls');
+ }
+}
+
+export default {
+ hidePaginationWhenNotNeeded,
+};
diff --git a/src/services/formValidation.js b/src/services/formValidation.js
index b268a9704..251fb725a 100644
--- a/src/services/formValidation.js
+++ b/src/services/formValidation.js
@@ -1,9 +1,16 @@
-function email(string) {
+export function email(string) {
// eslint-disable-next-line
const regex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return regex.test(string.toLowerCase());
}
+export function date(string) {
+ // eslint-disable-next-line
+ const regex = /[0-9]{4}-[0-9]{2}-[0-9]{2}/;
+ return regex.test(string);
+}
+
export default {
email,
+ date,
};
diff --git a/src/services/on-resize.js b/src/services/on-resize.js
new file mode 100644
index 000000000..4fab9789c
--- /dev/null
+++ b/src/services/on-resize.js
@@ -0,0 +1,35 @@
+import EventEmitter from '@/services/event-emitter';
+import throttle from 'lodash/throttle';
+import debounce from 'lodash/debounce';
+
+const throttledResize = throttle(() => {
+ EventEmitter.$emit('resize.throttled');
+}, 100);
+const debouncedResize = debounce(() => {
+ EventEmitter.$emit('resize.debounced');
+}, 300);
+
+window.addEventListener('resize', () => {
+ throttledResize();
+ debouncedResize();
+}, false);
+
+window.addEventListener('orientationchange', () => {
+ throttledResize();
+ debouncedResize();
+}, false);
+
+export default {
+ onThrottled: (cb) => {
+ EventEmitter.$on('resize.throttled', cb);
+ },
+ offThrottled: (cb) => {
+ EventEmitter.$off('resize.throttled', cb);
+ },
+ onDebounced: (cb) => {
+ EventEmitter.$on('resize.debounced', cb);
+ },
+ offDebounced: (cb) => {
+ EventEmitter.$off('resize.debounced', cb);
+ },
+};
diff --git a/src/services/utils.js b/src/services/utils.js
index 3b59a1f4c..173031209 100644
--- a/src/services/utils.js
+++ b/src/services/utils.js
@@ -20,26 +20,22 @@ export function populateTemplate(template, map) {
return template.replace(regex, match => map[match.slice(1)]);
}
-/** Takes a formData object and returns a formatted object, supports nesting
+/** Takes a data object and returns a nested object based on the keys and the nestingString
*/
-export function formatFormData(formData, { nestingEnabled = false, nestingString = '.' } = {}) {
+export function formatNestedData(data, nestingString = '.') {
const formattedObj = {};
- Object.keys(formData).forEach((fieldKey) => {
- const { value } = formData[fieldKey];
- if (!nestingEnabled) {
- formattedObj[fieldKey] = value;
- } else {
- const keys = fieldKey.split(nestingString);
- const nestedSubObj = keys.reverse().reduce((acc, currentKey, index) => {
- if (index === 0) {
- acc[currentKey] = value;
- return acc;
- }
- acc = merge({}, { [currentKey]: acc });
+ Object.keys(data).forEach((fieldKey) => {
+ const value = data[fieldKey];
+ const keys = fieldKey.split(nestingString);
+ const nestedSubObj = keys.reverse().reduce((acc, currentKey, index) => {
+ if (index === 0) {
+ acc[currentKey] = value;
return acc;
- }, {});
- merge(formattedObj, nestedSubObj);
- }
+ }
+ acc = merge({}, { [currentKey]: acc });
+ return acc;
+ }, {});
+ merge(formattedObj, nestedSubObj);
});
return formattedObj;
}
@@ -94,11 +90,17 @@ export const loadJSAsync = (e, n, o) => {
}, !1), r.parentNode.insertBefore(i, r)
};
+// Returns an array of the desired length filled with indexes as values
+export function getArrayOfSize(size = 0) {
+ return Array.from(Array(size), (_, x) => x);
+}
+
export default {
populateTemplate,
getPasswordStrength,
- formatFormData,
+ formatNestedData,
getRandomInt,
loadJSAsync,
accentFold,
+ getArrayOfSize,
};
diff --git a/src/stories/HdArrowButton.stories.js b/src/stories/HdArrowButton.stories.js
new file mode 100644
index 000000000..e15df4b32
--- /dev/null
+++ b/src/stories/HdArrowButton.stories.js
@@ -0,0 +1,60 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { storiesOf } from '@storybook/vue';
+import {
+ withKnobs, select, boolean,
+} from '@storybook/addon-knobs';
+
+import HdArrowButton from 'hd-blocks/components/buttons/HdArrowButton.vue';
+
+storiesOf('HdArrowButton', module)
+ .addDecorator(withKnobs)
+ .add('right', () => ({
+ components: { HdArrowButton },
+ data() {
+ return {
+ direction: 'right',
+ };
+ },
+ template: `
`,
+ }))
+ .add('left', () => ({
+ components: { HdArrowButton },
+ data() {
+ return {
+ direction: 'left',
+ };
+ },
+ template: `
`,
+ }))
+ .add('disabled', () => ({
+ components: { HdArrowButton },
+ data() {
+ return {
+ direction: 'left',
+ disabled: true,
+ };
+ },
+ template: `
`,
+ }))
+ .add('playground', () => ({
+ components: { HdArrowButton },
+ props: {
+ direction: {
+ default: select('Arrow direction', ['right', 'left'], 'right'),
+ },
+ disabled: {
+ default: boolean('Button disabled state', false),
+ },
+ },
+ template: `
`,
+ }));
diff --git a/src/stories/HdCheckbox.stories.js b/src/stories/HdCheckbox.stories.js
index 0b4383a21..cde12a634 100644
--- a/src/stories/HdCheckbox.stories.js
+++ b/src/stories/HdCheckbox.stories.js
@@ -1,17 +1,39 @@
/* eslint-disable import/no-extraneous-dependencies */
import { storiesOf } from '@storybook/vue';
-import { action } from '@storybook/addon-actions';
import HdCheckbox from 'hd-blocks/components/form/HdCheckbox.vue';
storiesOf('Form/HdCheckbox', module)
.add('required', () => ({
components: { HdCheckbox },
- template: '
',
- methods: { onDataChange: action('dataChange') },
+ template: `
+
+ `,
+ data() {
+ return {
+ isChecked: false,
+ };
+ },
}))
.add('checked', () => ({
components: { HdCheckbox },
- template: '
',
- methods: { onDataChange: action('dataChange') },
+ template: `
+
+ `,
+ data() {
+ return {
+ isChecked: true,
+ };
+ },
}));
diff --git a/src/stories/HdDetailsTable.stories.js b/src/stories/HdDetailsTable.stories.js
new file mode 100644
index 000000000..ec2decac1
--- /dev/null
+++ b/src/stories/HdDetailsTable.stories.js
@@ -0,0 +1,48 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { storiesOf } from '@storybook/vue';
+import { boolean } from '@storybook/addon-knobs';
+
+import HdDetailsTable from 'hd-blocks/components/details-table/HdDetailsTable.vue';
+import HdDetailsTableRow from 'hd-blocks/components/details-table/HdDetailsTableRow.vue';
+
+storiesOf('HdDetailsTable', module)
+ .add('default', () => ({
+ components: {
+ HdDetailsTable,
+ HdDetailsTableRow,
+ },
+ props: {
+ withDivider: {
+ type: Boolean,
+ default: boolean('With Divider', true),
+ },
+ forceSingleColumn: {
+ type: Boolean,
+ default: boolean('Single Column', false),
+ },
+ },
+ template: `
+
+
+
+ Chuck
+
+
+ Norris
+
+
+ 79
+
+
+
+ `,
+ }));
diff --git a/src/stories/HdDynamicForm.stories.js b/src/stories/HdDynamicForm.stories.js
index 54d2a5564..7895d08d5 100644
--- a/src/stories/HdDynamicForm.stories.js
+++ b/src/stories/HdDynamicForm.stories.js
@@ -3,7 +3,7 @@ import { storiesOf } from '@storybook/vue';
import HdDynamicForm from 'hd-blocks/components/form/HdDynamicForm.vue';
import FormWrapper from 'hd-blocks/storiesWrappers/FormWrapper';
-import { formatFormData } from 'hd-blocks/services/utils';
+import { formatNestedData } from 'hd-blocks/services/utils';
import CONFIG from './mocks/forms';
storiesOf('Form/HdDynamicForm', module)
@@ -17,54 +17,74 @@ storiesOf('Form/HdDynamicForm', module)
};
},
methods: {
- onSubmit(formData) {
- console.log('formData', formData);
- console.log('Formatted formData (nesting enabled)', formatFormData(formData, { nestingEnabled: true }));
+ onSubmit({ data, isValid, invalidFields }) {
+ console.log('invalidFields', invalidFields);
+ console.log('isValid', isValid);
+ console.log('data', data);
+ console.log(
+ 'Nested data',
+ formatNestedData(data),
+ );
},
},
}))
.add('signup', () => ({
components: { HdDynamicForm },
- template: '
',
+ template: '
',
data() {
return {
config: CONFIG.SIGNUP,
};
},
methods: {
- submit(formData) {
- console.log('formData', formData);
- console.log('Formatted formData (nesting enabled)', formatFormData(formData, { nestingEnabled: true }));
+ onSubmit({ data, isValid, invalidFields }) {
+ console.log('invalidFields', invalidFields);
+ console.log('isValid', isValid);
+ console.log('data', data);
+ console.log(
+ 'Nested data',
+ formatNestedData(data),
+ );
},
},
}))
.add('login', () => ({
components: { HdDynamicForm },
- template: '
',
+ template: '
',
data() {
return {
config: CONFIG.LOGIN,
};
},
methods: {
- submit(formData) {
- console.log('formData', formData);
- console.log('Formatted formData (nesting enabled)', formatFormData(formData, { nestingEnabled: true }));
+ onSubmit({ data, isValid, invalidFields }) {
+ console.log('invalidFields', invalidFields);
+ console.log('isValid', isValid);
+ console.log('data', data);
+ console.log(
+ 'Nested data',
+ formatNestedData(data),
+ );
},
},
}))
.add('specified language', () => ({
components: { HdDynamicForm },
- template: '
',
+ template: '
',
data() {
return {
config: CONFIG.LOGIN_EN,
};
},
methods: {
- submit(formData) {
- console.log('formData', formData);
- console.log('Formatted formData (nesting enabled)', formatFormData(formData, { nestingEnabled: true }));
+ onSubmit({ data, isValid, invalidFields }) {
+ console.log('invalidFields', invalidFields);
+ console.log('isValid', isValid);
+ console.log('data', data);
+ console.log(
+ 'Nested data',
+ formatNestedData(data),
+ );
},
},
}));
diff --git a/src/stories/HdEditSwitch.stories.js b/src/stories/HdEditSwitch.stories.js
new file mode 100644
index 000000000..6f28cf93d
--- /dev/null
+++ b/src/stories/HdEditSwitch.stories.js
@@ -0,0 +1,86 @@
+/* eslint-disable import/no-extraneous-dependencies, no-console */
+import { storiesOf } from '@storybook/vue';
+import { boolean } from '@storybook/addon-knobs';
+
+import HdEditSwitch from 'hd-blocks/components/HdEditSwitch.vue';
+import HdInput from 'hd-blocks/components/form/HdInput.vue';
+import FormWrapper from 'hd-blocks/storiesWrappers/FormWrapper';
+
+storiesOf('HdEditSwitch', module)
+ .addDecorator(FormWrapper)
+ .add('Concrete example', () => ({
+ components: { HdEditSwitch, HdInput },
+ template: `
+
+
+
+
+
+
+ First Name: {{ firstName }}
+ Last Name: {{ lastName }}
+
+
+ `,
+ props: {
+ isEditable: {
+ type: Boolean,
+ default: boolean('isEditable', true),
+ },
+ },
+ data() {
+ return {
+ firstName: 'Chuck',
+ lastName: 'Norris',
+ editedFirstName: '',
+ editedLastName: '',
+ editing: false,
+ loading: false,
+ };
+ },
+ created() {
+ this.editedFirstName = this.firstName;
+ this.editedLastName = this.lastName;
+ },
+ methods: {
+ onSave() {
+ const firstNameIsValid = this.$refs.firstName.validate();
+ const lastNameIsValid = this.$refs.lastName.validate();
+ this.loading = true;
+ if (firstNameIsValid && lastNameIsValid) {
+ setTimeout(this.onSaveSuccess, 2000);
+ }
+ },
+ onCancel() {
+ this.editing = false;
+ this.editedFirstName = this.firstName;
+ this.editedLastName = this.lastName;
+ },
+ onSaveSuccess() {
+ this.firstName = this.editedFirstName;
+ this.lastName = this.editedLastName;
+ this.editing = false;
+ this.loading = false;
+ },
+ enableEditing() {
+ this.editing = true;
+ },
+ },
+ }));
diff --git a/src/stories/HdExpandText.stories.js b/src/stories/HdExpandText.stories.js
new file mode 100644
index 000000000..f2bce1a1f
--- /dev/null
+++ b/src/stories/HdExpandText.stories.js
@@ -0,0 +1,103 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { storiesOf } from '@storybook/vue';
+import { number } from '@storybook/addon-knobs';
+
+import HdExpandText from 'hd-blocks/components/HdExpandText.vue';
+
+storiesOf('HdExpandText', module)
+ .add('default', () => ({
+ components: { HdExpandText },
+ props: {
+ lines: {
+ type: Number,
+ default: number('Lines', 4, {
+ range: true,
+ min: 1,
+ max: 20,
+ step: 1,
+ }),
+ },
+ linesMobile: {
+ type: Number,
+ default: number('Lines Mobile', 3, {
+ range: true,
+ min: 1,
+ max: 20,
+ step: 1,
+ }),
+ },
+ },
+ template: `
+
+
+ Ullamco. Cillum velit error, aliqua magni yet magni aute. Ad
+ velitesse, for eius nesciunt eaque, et. Aute quia dolores. Quo aute.
+ Minima incidunt nor in ea. Ullamco nihil, yet iste laboriosam nisi.
+ Incididunt. Perspiciatis nisi or culpa. Qui qui magnam so et but
+ labore so ullamco. Laudantium ea eiusmod, but lorem. Nesciunt cillum
+ nesciunt but consequat incidunt aspernatur. Voluptate consequatur.
+ Nequeporro. Quia. Quaerat aliquip, but unde, and perspiciatis adipisci
+ duis. Excepteur quam nor irure and aliquid or laboris, quis ipsum.
+ Doloremque beatae, beatae.
+
+
+ `,
+ }))
+ .add('custom translation', () => ({
+ components: { HdExpandText },
+ props: {
+ lines: {
+ type: Number,
+ default: number('Lines', 4, {
+ range: true,
+ min: 1,
+ max: 20,
+ step: 1,
+ }),
+ },
+ linesMobile: {
+ type: Number,
+ default: number('Lines Mobile', 3, {
+ range: true,
+ min: 1,
+ max: 20,
+ step: 1,
+ }),
+ },
+ },
+ data() {
+ return {
+ value: '',
+ texts: {
+ EXPAND_TEXT: {
+ TOGGLE: {
+ SHOW_MORE: 'I need more!',
+ SHOW_LESS: 'Ughh... Close it please',
+ },
+ },
+ },
+ };
+ },
+ template: `
+
+
+ Ullamco. Cillum velit error, aliqua magni yet magni aute. Ad
+ velitesse, for eius nesciunt eaque, et. Aute quia dolores. Quo aute.
+ Minima incidunt nor in ea. Ullamco nihil, yet iste laboriosam nisi.
+ Incididunt. Perspiciatis nisi or culpa. Qui qui magnam so et but
+ labore so ullamco. Laudantium ea eiusmod, but lorem. Nesciunt cillum
+ nesciunt but consequat incidunt aspernatur. Voluptate consequatur.
+ Nequeporro. Quia. Quaerat aliquip, but unde, and perspiciatis adipisci
+ duis. Excepteur quam nor irure and aliquid or laboris, quis ipsum.
+ Doloremque beatae, beatae.
+
+
+ `,
+ }));
diff --git a/src/stories/HdGallery.stories.js b/src/stories/HdGallery.stories.js
new file mode 100644
index 000000000..469226f8c
--- /dev/null
+++ b/src/stories/HdGallery.stories.js
@@ -0,0 +1,29 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { storiesOf } from '@storybook/vue';
+import { action } from '@storybook/addon-actions';
+
+import HdGallery from 'hd-blocks/components/gallery/HdGallery.vue';
+import ITEMS from './mocks/GALLERY_ITEMS';
+
+storiesOf('HdGallery', module)
+ .add('default', () => ({
+ components: { HdGallery },
+ template: `
+
+
+
+ `,
+ data() {
+ return {
+ items: ITEMS,
+ };
+ },
+ methods: {
+ onInput(value) {
+ action('input')(value);
+ },
+ },
+ }));
diff --git a/src/stories/HdInfoBox.stories.js b/src/stories/HdInfoBox.stories.js
new file mode 100644
index 000000000..145e2545c
--- /dev/null
+++ b/src/stories/HdInfoBox.stories.js
@@ -0,0 +1,12 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { storiesOf } from '@storybook/vue';
+
+import HdInfoBox from 'hd-blocks/components/HdInfoBox.vue';
+
+storiesOf('HdInfoBox', module)
+ .add('default', () => ({
+ components: { HdInfoBox },
+ template: `
+
+ `,
+ }));
diff --git a/src/stories/HdInput.stories.js b/src/stories/HdInput.stories.js
index 422f49ea1..23510d426 100644
--- a/src/stories/HdInput.stories.js
+++ b/src/stories/HdInput.stories.js
@@ -9,21 +9,141 @@ storiesOf('Form/HdInput', module)
.addDecorator(FormWrapper)
.add('required', () => ({
components: { HdInput },
- template: '
',
- methods: { onDataChange: action('dataChange') },
+ template: `
+
+ `,
+ data() {
+ return {
+ value: '',
+ };
+ },
+ watch: {
+ value(value) {
+ action('input')(value);
+ },
+ },
}))
.add('prefilled', () => ({
components: { HdInput },
- template: '
',
- methods: { onDataChange: action('dataChange') },
+ template: `
+
+ `,
+ data() {
+ return {
+ value: 'Your default value goes here',
+ };
+ },
+ watch: {
+ value(value) {
+ action('input')(value);
+ },
+ },
}))
.add('password', () => ({
components: { HdInput },
- template: '
',
- methods: { onDataChange: action('dataChange') },
+ template: `
+
+ `,
+ data() {
+ return {
+ value: '',
+ };
+ },
+ watch: {
+ value(value) {
+ action('input')(value);
+ },
+ },
}))
.add('email', () => ({
components: { HdInput },
- template: '
',
- methods: { onDataChange: action('dataChange') },
+ template: `
+
+ `,
+ data() {
+ return {
+ value: '',
+ };
+ },
+ watch: {
+ value(value) {
+ action('input')(value);
+ },
+ },
+ }))
+ .add('number', () => ({
+ components: { HdInput },
+ template: `
+
+ `,
+ data() {
+ return {
+ value: '',
+ };
+ },
+ watch: {
+ value(value) {
+ action('input')(value);
+ },
+ },
+ }))
+ .add('custom translation', () => ({
+ components: { HdInput },
+ template: `
+
+ `,
+ data() {
+ return {
+ value: '',
+ texts: {
+ FORM: {
+ VALIDATION: {
+ REQUIRED: 'Hey you, yeah you! Fill it out!',
+ },
+ },
+ },
+ };
+ },
+ watch: {
+ value(value) {
+ action('input')(value);
+ },
+ },
}));
diff --git a/src/stories/HdLoaderButton.stories.js b/src/stories/HdLoaderButton.stories.js
index 8eccab507..0756af394 100644
--- a/src/stories/HdLoaderButton.stories.js
+++ b/src/stories/HdLoaderButton.stories.js
@@ -8,14 +8,32 @@ import HdLoaderButton from 'hd-blocks/components/buttons/HdLoaderButton.vue';
storiesOf('HdLoaderButton', module)
.add('with label', () => ({
components: { HdLoaderButton },
- data: () => ({
- label: text('Label', 'Loader Button Label'),
- isStatic: boolean('Static?', true),
- resetOnSuccess: boolean('Reset on success state?', true),
- idleResetTime: number('Reset time?', 200),
- height: number('Height', 46),
- loadingCircleStrokeWidth: number('Loading circle stroke width', 4),
- }),
+ props: {
+ label: {
+ type: String,
+ default: text('Label', 'Loader Button Label'),
+ },
+ isStatic: {
+ type: Boolean,
+ default: boolean('Static?', true),
+ },
+ resetOnSuccess: {
+ type: Boolean,
+ default: boolean('Reset on success state?', true),
+ },
+ idleResetTime: {
+ type: Number,
+ default: number('Reset time?', 200),
+ },
+ height: {
+ type: Number,
+ default: number('Height', 46),
+ },
+ loadingCircleStrokeWidth: {
+ type: Number,
+ default: number('Loading circle stroke width', 4),
+ },
+ },
template: `
({
+ components: { HdNotifications },
+ props: {
+ type: {
+ type: String,
+ default: select(
+ 'Type',
+ [
+ 'notification',
+ 'success',
+ 'error',
+ 'warning',
+ ],
+ 'notification',
+ ),
+ },
+ },
+ template: `
+
+
+
+ `,
+ data() {
+ return {
+ notifications: [
+ {
+ id: 0,
+ message: 'Hello world!, This is a link... ',
+ },
+ ],
+ };
+ },
+ computed: {
+ transformedNotifications() {
+ return this.notifications.map(notification => ({
+ ...notification,
+ type: this.type,
+ }));
+ },
+ },
+ methods: {
+ onRoute(route) {
+ action('route')(route);
+ },
+ onHeightChange(height) {
+ action('heightChange')(height);
+ },
+ },
+ }), {
+ notes: {
+ markdown: HdNotificationsNote,
+ },
+ })
+ .add('custom icon', () => ({
+ components: { HdNotifications },
+ props: {
+ type: {
+ type: String,
+ default: select(
+ 'Type',
+ [
+ 'notification',
+ 'success',
+ 'error',
+ 'warning',
+ ],
+ 'warning',
+ ),
+ },
+ },
+ template: `
+
+
+
+ `,
+ data() {
+ return {
+ notifications: [
+ {
+ id: 0,
+ message: 'Hello world!, This is a link... ',
+ },
+ ],
+ // eslint-disable-next-line global-require
+ customIcon: require('./assets/homeday-icon.svg'),
+ };
+ },
+ computed: {
+ transformedNotifications() {
+ return this.notifications.map(notification => ({
+ ...notification,
+ type: this.type,
+ customIcon: this.customIcon,
+ }));
+ },
+ },
+ methods: {
+ onRoute(route) {
+ action('route')(route);
+ },
+ onHeightChange(height) {
+ action('heightChange')(height);
+ },
+ },
+ }));
diff --git a/src/stories/HdPager.stories.js b/src/stories/HdPager.stories.js
new file mode 100644
index 000000000..13bca2acc
--- /dev/null
+++ b/src/stories/HdPager.stories.js
@@ -0,0 +1,43 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { storiesOf } from '@storybook/vue';
+import { action } from '@storybook/addon-actions';
+import { number } from '@storybook/addon-knobs';
+
+import HdPager from 'hd-blocks/components/HdPager.vue';
+
+storiesOf('HdPager', module)
+ .add('default', () => ({
+ components: { HdPager },
+ props: {
+ count: {
+ type: Number,
+ default: number('Count', 10),
+ },
+ maxVisible: {
+ type: Number,
+ default: number('Max Visible', 7, {
+ range: true,
+ min: 3,
+ max: 20,
+ step: 1,
+ }),
+ },
+ },
+ template: `
+
+ `,
+ data() {
+ return {
+ page: 0,
+ };
+ },
+ watch: {
+ page(page) {
+ action('input')(page);
+ },
+ },
+ }));
diff --git a/src/stories/HdPasswordConfirm.stories.js b/src/stories/HdPasswordConfirm.stories.js
index d7770d5c9..6d985cdf6 100644
--- a/src/stories/HdPasswordConfirm.stories.js
+++ b/src/stories/HdPasswordConfirm.stories.js
@@ -10,36 +10,78 @@ storiesOf('Form/HdPasswordConfirm', module)
.addDecorator(FormWrapper)
.add('simple', () => ({
components: { HdPasswordConfirm },
- template: ' ',
+ template: `
+
+ `,
methods: { onDataChange: action('dataChange') },
}))
- .add('min characters (10)', () => ({
+ .add('with validity check', () => ({
components: { HdPasswordConfirm },
- template: ' ',
- methods: { onDataChange: action('dataChange') },
+ props: {
+ min: {
+ type: Number,
+ default: number('min', 5),
+ },
+ },
+ template: `
+
+
+
Check Validity
+
Is valid: {{ valid }}
+
+ `,
+ methods: {
+ check() {
+ this.valid = this.$refs.password.validate();
+ },
+ },
data() {
return {
- min: number('min', 6),
+ password: '',
+ valid: false,
};
},
}))
- .add('with strength UI', () => ({
+ .add('with strength bar style', () => ({
components: { HdPasswordConfirm },
- template: ' ',
+ template: `
+
+ `,
methods: { onDataChange: action('dataChange') },
- data() {
- return {
- withStrength: boolean('withStrength', false),
- };
+ props: {
+ withStrength: {
+ type: Boolean,
+ default: boolean('withStrength', true),
+ },
+ },
+ watch: {
+
},
}))
.add('without strength', () => ({
components: { HdPasswordConfirm },
- template: ' ',
+ template: `
+
+ `,
methods: { onDataChange: action('dataChange') },
- data() {
- return {
- withStrength: boolean('withStrength', false),
- };
+ props: {
+ withStrength: {
+ type: Boolean,
+ default: boolean('withStrength', false),
+ },
},
}));
diff --git a/src/stories/HdRadio.stories.js b/src/stories/HdRadio.stories.js
index f89605f27..789206f6d 100644
--- a/src/stories/HdRadio.stories.js
+++ b/src/stories/HdRadio.stories.js
@@ -3,54 +3,97 @@ import { storiesOf } from '@storybook/vue';
import { action } from '@storybook/addon-actions';
import HdRadio from 'hd-blocks/components/form/HdRadio.vue';
-import ITEMS from './mocks/RADIOS';
+import ITEMS from './mocks/FORM_ITEMS';
storiesOf('Form/HdRadio', module)
.add('required', () => ({
components: { HdRadio },
template: `
-
-
- Validate (Check action logger)
-
+
+
+
+ Validate (Check action logger)
+
+
`,
- methods: {
- onDataChange({ value, error }) {
- this.radioData = { value, error };
+ data() {
+ return {
+ value: '',
+ ITEMS,
+ };
+ },
+ watch: {
+ value(value) {
+ action('input')(value);
},
+ },
+ methods: {
validate() {
- this.$refs.myRadio.validityCheck();
- if (this.radioData.error) {
+ const isValid = this.$refs.myRadio.validate();
+
+ if (!isValid) {
console.log('Not Valid');
} else {
- console.log(`Valid! You selected: ${this.radioData.value}`);
+ console.log(`Valid! You selected: ${this.value}`);
}
},
},
- data() {
- return {
- ITEMS,
- radioData: null,
- };
- },
}))
.add('preselected', () => ({
components: { HdRadio },
- template: ' ',
- methods: { onDataChange: action('dataChange') },
+ template: `
+
+
+
+ `,
data() {
return {
+ value: ITEMS[2].value,
ITEMS,
};
},
+ watch: {
+ value(value) {
+ action('input')(value);
+ },
+ },
}))
.add('vertical', () => ({
components: { HdRadio },
- template: ' ',
- methods: { onDataChange: action('dataChange') },
+ template: `
+
+
+
+ `,
data() {
return {
+ value: '',
ITEMS,
};
},
+ watch: {
+ value(value) {
+ action('input')(value);
+ },
+ },
}));
diff --git a/src/stories/HdRadioButton.stories.js b/src/stories/HdRadioButton.stories.js
index 8cdcbff42..e941ba160 100644
--- a/src/stories/HdRadioButton.stories.js
+++ b/src/stories/HdRadioButton.stories.js
@@ -2,21 +2,11 @@
import { storiesOf } from '@storybook/vue';
import { action } from '@storybook/addon-actions';
import { text } from '@storybook/addon-knobs';
-
import HdRadioButton from 'hd-blocks/components/buttons/HdRadioButton.vue';
-import HdRadioButtonNote from '../notes/HdRadioButton.md';
storiesOf('HdRadioButton', module)
.add('base', () => ({
components: { HdRadioButton },
- data: () => ({
- label: text('label', 'Test Label'),
- desktopIcon: text('desktopIcon', 'https://via.placeholder.com/96'),
- desktopIconHover: text('desktopIconHover', 'https://via.placeholder.com/96?text=Hover'),
- mobileIcon: text('mobileIcon', 'https://via.placeholder.com/96'),
- value: text('value', 'Value'),
- name: text('name', 'Name'),
- }),
template: ` `,
+ props: {
+ label: {
+ type: String,
+ default: text('label', 'Test Label'),
+ },
+ desktopIcon: {
+ type: String,
+ default: text('desktopIcon', 'https://via.placeholder.com/96'),
+ },
+ desktopIconHover: {
+ type: String,
+ default: text('desktopIconHover', 'https://via.placeholder.com/96?text=Hover'),
+ },
+ mobileIcon: {
+ type: String,
+ default: text('mobileIcon', 'https://via.placeholder.com/96'),
+ },
+ value: {
+ type: String,
+ default: text('value', 'Value'),
+ },
+ name: {
+ type: String,
+ default: text('name', 'Name'),
+ },
+ },
methods: { action: action('clicked') },
- }), { notes: { markdown: HdRadioButtonNote } });
+ }));
diff --git a/src/stories/HdSelect.stories.js b/src/stories/HdSelect.stories.js
new file mode 100644
index 000000000..7e31ca2ac
--- /dev/null
+++ b/src/stories/HdSelect.stories.js
@@ -0,0 +1,74 @@
+/* eslint-disable import/no-extraneous-dependencies, no-console */
+import { storiesOf } from '@storybook/vue';
+import { action } from '@storybook/addon-actions';
+
+import HdSelect from 'hd-blocks/components/form/HdSelect.vue';
+import FormWrapper from 'hd-blocks/storiesWrappers/FormWrapper';
+import ITEMS from './mocks/FORM_ITEMS';
+
+storiesOf('Form/HdSelect', module)
+ .addDecorator(FormWrapper)
+ .add('required', () => ({
+ components: { HdSelect },
+ template: `
+
+
+
+ Validate (Check action logger)
+
+
+ `,
+ data() {
+ return {
+ value: '',
+ ITEMS,
+ };
+ },
+ watch: {
+ value(value) {
+ action('input')(value);
+ },
+ },
+ methods: {
+ validate() {
+ const isValid = this.$refs.myRadio.validate();
+
+ if (!isValid) {
+ console.log('Not Valid');
+ } else {
+ console.log(`Valid! You selected: ${this.value}`);
+ }
+ },
+ },
+ }))
+ .add('preselected', () => ({
+ components: { HdSelect },
+ template: `
+
+
+
+ `,
+ data() {
+ return {
+ value: ITEMS[2].value,
+ ITEMS,
+ };
+ },
+ watch: {
+ value(value) {
+ action('input')(value);
+ },
+ },
+ }));
diff --git a/src/stories/HdTable.stories.js b/src/stories/HdTable.stories.js
index 582ea7b1e..bc543880d 100644
--- a/src/stories/HdTable.stories.js
+++ b/src/stories/HdTable.stories.js
@@ -2,7 +2,7 @@
import { storiesOf } from '@storybook/vue';
import HdTable from 'hd-blocks/components/HdTable.vue';
-import HdInput from 'hd-blocks/components/form/HdInput.vue';
+import HdTagsList from 'hd-blocks/components/HdTagsList.vue';
import TableWrapper from 'hd-blocks/storiesWrappers/TableWrapper';
import MOVIES_TABLE from 'hd-blocks/stories/mocks/tables/movies';
@@ -45,7 +45,10 @@ storiesOf('HdTable', module)
},
}))
.add('with components', () => ({
- components: { HdTable, HdInput },
+ components: {
+ HdTable,
+ HdTagsList,
+ },
template: ' ',
data() {
return {
@@ -54,15 +57,14 @@ storiesOf('HdTable', module)
title, year, rating, stars,
}) => ({
title,
+ year,
+ rating,
theNameDoesntMatter: {
- component: HdInput,
+ component: HdTagsList,
props: {
- value: year,
- required: true,
+ items: stars.split(','),
},
},
- rating,
- stars,
})),
};
},
diff --git a/src/stories/HdTabsMenu.stories.js b/src/stories/HdTabsMenu.stories.js
new file mode 100644
index 000000000..6c7715aff
--- /dev/null
+++ b/src/stories/HdTabsMenu.stories.js
@@ -0,0 +1,30 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { storiesOf } from '@storybook/vue';
+import { action } from '@storybook/addon-actions';
+
+import HdTabsMenu from 'hd-blocks/components/HdTabsMenu.vue';
+import ITEMS from './mocks/MENU_ITEMS';
+
+storiesOf('HdTabsMenu', module)
+ .add('default', () => ({
+ components: { HdTabsMenu },
+ template: `
+
+
+
+ `,
+ data() {
+ return {
+ value: '',
+ items: ITEMS,
+ };
+ },
+ watch: {
+ value(value) {
+ action('input')(value);
+ },
+ },
+ }));
diff --git a/src/stories/HdTagsList.stories.js b/src/stories/HdTagsList.stories.js
new file mode 100644
index 000000000..6934b0700
--- /dev/null
+++ b/src/stories/HdTagsList.stories.js
@@ -0,0 +1,23 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { storiesOf } from '@storybook/vue';
+import { array } from '@storybook/addon-knobs';
+
+import HdTagsList from 'hd-blocks/components/HdTagsList.vue';
+import ITEMS from './mocks/FORM_ITEMS';
+
+
+storiesOf('HdTagsList', module)
+ .add('default', () => ({
+ components: { HdTagsList },
+ template: `
+
+ `,
+ props: {
+ items: {
+ type: Array,
+ default: array('items', ITEMS.map(({ label }) => label)),
+ },
+ },
+ }));
diff --git a/src/stories/HdTagsSelector.stories.js b/src/stories/HdTagsSelector.stories.js
new file mode 100644
index 000000000..7720b2e75
--- /dev/null
+++ b/src/stories/HdTagsSelector.stories.js
@@ -0,0 +1,32 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { storiesOf } from '@storybook/vue';
+import { action } from '@storybook/addon-actions';
+
+import HdTagsSelector from 'hd-blocks/components/form/HdTagsSelector.vue';
+import ITEMS from './mocks/FORM_ITEMS';
+
+
+storiesOf('Form/HdTagsSelector', module)
+ .add('prefilled', () => ({
+ components: { HdTagsSelector },
+ template: `
+
+ `,
+ data() {
+ return {
+ selectedItems: [
+ ITEMS[0],
+ ITEMS[2],
+ ],
+ allItems: ITEMS,
+ };
+ },
+ watch: {
+ selectedItems(selectedItems) {
+ action('selectedItems')(selectedItems);
+ },
+ },
+ }));
diff --git a/src/stories/HdTextarea.stories.js b/src/stories/HdTextarea.stories.js
new file mode 100644
index 000000000..16c905fb2
--- /dev/null
+++ b/src/stories/HdTextarea.stories.js
@@ -0,0 +1,82 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { storiesOf } from '@storybook/vue';
+import { action } from '@storybook/addon-actions';
+
+import HdTextarea from 'hd-blocks/components/form/HdTextarea.vue';
+import FormWrapper from 'hd-blocks/storiesWrappers/FormWrapper';
+
+storiesOf('Form/HdTextarea', module)
+ .addDecorator(FormWrapper)
+ .add('required', () => ({
+ components: { HdTextarea },
+ template: `
+
+ `,
+ data() {
+ return {
+ value: '',
+ };
+ },
+ watch: {
+ value(value) {
+ action('input')(value);
+ },
+ },
+ }))
+ .add('prefilled', () => ({
+ components: { HdTextarea },
+ template: `
+
+ `,
+ data() {
+ return {
+ value: 'Your default value goes here',
+ };
+ },
+ watch: {
+ value(value) {
+ action('input')(value);
+ },
+ },
+ }))
+ .add('custom translation', () => ({
+ components: { HdTextarea },
+ template: `
+
+ `,
+ data() {
+ return {
+ value: '',
+ texts: {
+ FORM: {
+ VALIDATION: {
+ REQUIRED: 'Hey you, yeah you! Fill it out!',
+ },
+ },
+ },
+ };
+ },
+ watch: {
+ value(value) {
+ action('input')(value);
+ },
+ },
+ }));
diff --git a/src/stories/HdTimeslots.stories.js b/src/stories/HdTimeslots.stories.js
index fc0b5c790..f66fe38f1 100644
--- a/src/stories/HdTimeslots.stories.js
+++ b/src/stories/HdTimeslots.stories.js
@@ -37,3 +37,6 @@ storiesOf('HdTimeslots', module)
/>`,
methods: { action: action('timeSelected') },
}));
+
+
+export default generateSlots;
diff --git a/src/stories/HdToast.stories.js b/src/stories/HdToast.stories.js
index a311cd5dc..60d1e87d1 100644
--- a/src/stories/HdToast.stories.js
+++ b/src/stories/HdToast.stories.js
@@ -6,14 +6,54 @@ import { text } from '@storybook/addon-knobs';
import HdToast from 'hd-blocks/components/HdToast.vue';
storiesOf('HdToast', module)
- .add('required', () => ({
+ .add('Primary only', () => ({
components: { HdToast },
data: () => ({
- text: text('Toast text', 'textfafafsasf'),
+ text: text('Toast text', 'Random text'),
}),
template: `
- {{text}}
+ {{text}}
+ Show Toast!
+
+ `,
+ methods: {
+ primaryClick: action('primaryClick'),
+ },
+ }), {
+ knobs: {
+ escapeHTML: false,
+ },
+ })
+ .add('Secondary only', () => ({
+ components: { HdToast },
+ data: () => ({
+ text: text('Toast text', 'Random text'),
+ }),
+ template: `
+
+ {{text}}
+ Show Toast!
+
+ `,
+ methods: {
+ onClose: action('closed'),
+ primaryClick: action('primaryClick'),
+ secondaryClick: action('secondaryClick'),
+ },
+ }), {
+ knobs: {
+ escapeHTML: false,
+ },
+ })
+ .add('Complete', () => ({
+ components: { HdToast },
+ data: () => ({
+ text: text('Toast text', 'Random text'),
+ }),
+ template: `
+
+ {{text}}
Show Toast!
`,
diff --git a/src/stories/assets/homeday-icon.svg b/src/stories/assets/homeday-icon.svg
new file mode 100644
index 000000000..32a160b3b
--- /dev/null
+++ b/src/stories/assets/homeday-icon.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/stories/mocks/FORM_ITEMS.js b/src/stories/mocks/FORM_ITEMS.js
new file mode 100644
index 000000000..8c4853a79
--- /dev/null
+++ b/src/stories/mocks/FORM_ITEMS.js
@@ -0,0 +1,22 @@
+export default [
+ {
+ label: 'Jon "Snow"',
+ value: 'jon',
+ },
+ {
+ label: 'Daenerys',
+ value: 'daenerys',
+ },
+ {
+ label: 'The Night King',
+ value: 'nightKing',
+ },
+ {
+ label: 'Arya',
+ value: 'arya',
+ },
+ {
+ label: 'Cersei',
+ value: 'cersei',
+ },
+];
diff --git a/src/stories/mocks/GALLERY_ITEMS.js b/src/stories/mocks/GALLERY_ITEMS.js
new file mode 100644
index 000000000..f892fc3f0
--- /dev/null
+++ b/src/stories/mocks/GALLERY_ITEMS.js
@@ -0,0 +1,67 @@
+export default [
+ {
+ image: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2Fadc5cc05-4b0c-4b85-b324-a115358e405e-1262338255.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=1600&h=900&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=c6f8884b2baee1486134be70817b4a65',
+ thumbnail: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2Fadc5cc05-4b0c-4b85-b324-a115358e405e-1262338255.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=372&h=210&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=ffc3431ee910b43c98c8c0b2128dd52d',
+ caption: 'Some title',
+ }, {
+ image: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F9ce77ae5-f5cb-4529-a828-4e60b4fe179a-1262338250.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=1600&h=900&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=b14e660c69dcde4ec7785765bca30f19',
+ thumbnail: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F9ce77ae5-f5cb-4529-a828-4e60b4fe179a-1262338250.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=372&h=210&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=8f172444a4ab72d28988eb38733aae07',
+ caption: '__vue_devtool_undefined__',
+ }, {
+ image: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F744996e5-fec2-4a02-98e7-c2ce1e722946-1262338252.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=900&h=1600&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=62f7a5210386e4bb7d6f5fe82a126126',
+ thumbnail: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F744996e5-fec2-4a02-98e7-c2ce1e722946-1262338252.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=210&h=372&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=a999b86e7d7e3122bb7a633296d57e81',
+ caption: 'Some other title',
+ }, {
+ image: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F2c7d0043-4dd3-4172-aefa-a42b048dde14-1262338251.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=1600&h=900&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=1c4c05d190155b19ad26a4bd98d8b84b',
+ thumbnail: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F2c7d0043-4dd3-4172-aefa-a42b048dde14-1262338251.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=372&h=210&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=27c8ed1fd9215cbdc1d243992cf803c2',
+ caption: 'Kitchen',
+ }, {
+ image: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F048ef2e1-c561-4eff-8a6b-b74b4af3ad18-1262338249.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=900&h=1600&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=54d18d6e993067e33c54c40b8d4eff5a',
+ thumbnail: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F048ef2e1-c561-4eff-8a6b-b74b4af3ad18-1262338249.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=210&h=372&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=eec9caf224545e117240263010004cd9',
+ caption: 'Living room',
+ }, {
+ image: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2Fae1c317a-5fc2-48da-9bd5-20fb1d9e22a9-1262338256.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=1600&h=900&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=29c732eb84f4ad7143931e294d477709',
+ thumbnail: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2Fae1c317a-5fc2-48da-9bd5-20fb1d9e22a9-1262338256.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=372&h=210&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=f9b31e5c5c4a6a9b23b3a45ab48b7a25',
+ caption: 'Cellar',
+ }, {
+ image: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F63d89792-c618-4af3-a402-701e7fe8e050-1262338258.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=1600&h=900&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=9be8bccda6af948f3dc808aa3b3d9a7d',
+ thumbnail: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F63d89792-c618-4af3-a402-701e7fe8e050-1262338258.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=372&h=210&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=fe3904552f03982c10763b5e0fc54d96',
+ caption: '__vue_devtool_undefined__',
+ }, {
+ image: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2Fa1e235f4-2a42-4242-9a8e-2a3eeca6268b-1262338261.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=900&h=1600&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=233c9ca90ddf8d0c01a5ed8f512dfff4',
+ thumbnail: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2Fa1e235f4-2a42-4242-9a8e-2a3eeca6268b-1262338261.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=210&h=372&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=bcb0e01b67e67e038b23e0fd6ba7c2b9',
+ caption: '__vue_devtool_undefined__',
+ }, {
+ image: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2Fadc5cc05-4b0c-4b85-b324-a115358e405e-1262338255.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=1600&h=900&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=c6f8884b2baee1486134be70817b4a65',
+ thumbnail: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2Fadc5cc05-4b0c-4b85-b324-a115358e405e-1262338255.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=372&h=210&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=ffc3431ee910b43c98c8c0b2128dd52d',
+ caption: 'Some title',
+ }, {
+ image: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F9ce77ae5-f5cb-4529-a828-4e60b4fe179a-1262338250.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=1600&h=900&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=b14e660c69dcde4ec7785765bca30f19',
+ thumbnail: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F9ce77ae5-f5cb-4529-a828-4e60b4fe179a-1262338250.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=372&h=210&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=8f172444a4ab72d28988eb38733aae07',
+ caption: 'Some title',
+ }, {
+ image: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F744996e5-fec2-4a02-98e7-c2ce1e722946-1262338252.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=900&h=1600&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=62f7a5210386e4bb7d6f5fe82a126126',
+ thumbnail: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F744996e5-fec2-4a02-98e7-c2ce1e722946-1262338252.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=210&h=372&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=a999b86e7d7e3122bb7a633296d57e81',
+ caption: 'Some title',
+ }, {
+ image: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F2c7d0043-4dd3-4172-aefa-a42b048dde14-1262338251.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=1600&h=900&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=1c4c05d190155b19ad26a4bd98d8b84b',
+ thumbnail: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F2c7d0043-4dd3-4172-aefa-a42b048dde14-1262338251.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=372&h=210&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=27c8ed1fd9215cbdc1d243992cf803c2',
+ caption: 'Some title',
+ }, {
+ image: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F048ef2e1-c561-4eff-8a6b-b74b4af3ad18-1262338249.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=900&h=1600&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=54d18d6e993067e33c54c40b8d4eff5a',
+ thumbnail: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F048ef2e1-c561-4eff-8a6b-b74b4af3ad18-1262338249.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=210&h=372&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=eec9caf224545e117240263010004cd9',
+ caption: 'Some title',
+ }, {
+ image: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2Fae1c317a-5fc2-48da-9bd5-20fb1d9e22a9-1262338256.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=1600&h=900&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=29c732eb84f4ad7143931e294d477709',
+ thumbnail: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2Fae1c317a-5fc2-48da-9bd5-20fb1d9e22a9-1262338256.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=372&h=210&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=f9b31e5c5c4a6a9b23b3a45ab48b7a25',
+ caption: 'Some title',
+ }, {
+ image: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F63d89792-c618-4af3-a402-701e7fe8e050-1262338258.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=1600&h=900&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=9be8bccda6af948f3dc808aa3b3d9a7d',
+ thumbnail: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2F63d89792-c618-4af3-a402-701e7fe8e050-1262338258.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=372&h=210&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=fe3904552f03982c10763b5e0fc54d96',
+ caption: 'Some title',
+ }, {
+ image: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2Fa1e235f4-2a42-4242-9a8e-2a3eeca6268b-1262338261.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=900&h=1600&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=233c9ca90ddf8d0c01a5ed8f512dfff4',
+ thumbnail: 'https://homeday.imgix.net/https%3A%2F%2Fpictures.immobilienscout24.de%2Flistings%2Fa1e235f4-2a42-4242-9a8e-2a3eeca6268b-1262338261.jpg%2FORIG%2Fformat%2Fjpg%2Fquality%2F80?w=210&h=372&fit=crop&crop=focalpoint&ixlib=js-1.2.0&s=bcb0e01b67e67e038b23e0fd6ba7c2b9',
+ caption: 'Some title',
+ },
+];
diff --git a/src/stories/mocks/MENU_ITEMS.js b/src/stories/mocks/MENU_ITEMS.js
new file mode 100644
index 000000000..8c4853a79
--- /dev/null
+++ b/src/stories/mocks/MENU_ITEMS.js
@@ -0,0 +1,22 @@
+export default [
+ {
+ label: 'Jon "Snow"',
+ value: 'jon',
+ },
+ {
+ label: 'Daenerys',
+ value: 'daenerys',
+ },
+ {
+ label: 'The Night King',
+ value: 'nightKing',
+ },
+ {
+ label: 'Arya',
+ value: 'arya',
+ },
+ {
+ label: 'Cersei',
+ value: 'cersei',
+ },
+];
diff --git a/src/stories/mocks/RADIOS.js b/src/stories/mocks/RADIOS.js
deleted file mode 100644
index 8f9b38262..000000000
--- a/src/stories/mocks/RADIOS.js
+++ /dev/null
@@ -1,18 +0,0 @@
-export default [
- {
- label: 'Green',
- value: 'green',
- },
- {
- label: 'Blue',
- value: 'blue',
- },
- {
- label: 'Red',
- value: 'red',
- },
- {
- label: 'Yellow',
- value: 'yellow',
- },
-];
diff --git a/src/stories/mocks/forms/PROFILE.js b/src/stories/mocks/forms/PROFILE.js
index 34f98b7a8..d00dadb60 100644
--- a/src/stories/mocks/forms/PROFILE.js
+++ b/src/stories/mocks/forms/PROFILE.js
@@ -2,9 +2,9 @@ export default [
{
type: 'radio',
name: 'gender',
+ initialValue: 'male',
props: {
required: true,
- selected: 'male',
items: [
{
label: 'Female',
@@ -20,9 +20,9 @@ export default [
{
type: 'input',
name: 'first_name',
+ initialValue: 'First name',
props: {
required: true,
- value: 'First name',
label: 'Vorname',
placeholder: 'First name...',
},
@@ -30,9 +30,9 @@ export default [
{
type: 'input',
name: 'last_name',
+ initialValue: 'Last name',
props: {
required: true,
- value: 'Last name',
label: 'Nachname',
placeholder: 'Last name...',
},
@@ -40,9 +40,9 @@ export default [
{
type: 'input',
name: 'email',
+ initialValue: 'kontakt@homeday.de',
props: {
required: true,
- value: 'kontakt@homeday.de',
type: 'email',
label: 'E-Mail-Adresse',
placeholder: 'Email...',
@@ -51,9 +51,9 @@ export default [
{
type: 'input',
name: 'landline_number',
+ initialValue: '',
props: {
required: false,
- value: '',
type: 'tel',
label: 'Telefonnummer',
placeholder: 'Lanline number...',
@@ -62,9 +62,9 @@ export default [
{
type: 'input',
name: 'mobile_number',
+ initialValue: '030 120 849 110',
props: {
required: true,
- value: '030 120 849 110',
type: 'tel',
label: 'Handynummer',
placeholder: 'Phone number...',
@@ -73,9 +73,9 @@ export default [
{
type: 'input',
name: 'address.street',
+ initialValue: 'Prinzessinnenstraße',
props: {
required: false,
- value: 'Prinzessinnenstraße',
type: 'text',
label: 'Straße',
placeholder: 'Street...',
@@ -85,9 +85,9 @@ export default [
{
type: 'input',
name: 'address.house_number',
+ initialValue: '26',
props: {
required: false,
- value: '26',
type: 'text',
label: 'Hausnummer',
placeholder: 'House number...',
@@ -96,9 +96,9 @@ export default [
{
type: 'input',
name: 'address.zip_code',
+ initialValue: '10969',
props: {
required: false,
- value: '10969',
type: 'text',
label: 'PLZ',
placeholder: 'Zip code...',
@@ -108,9 +108,9 @@ export default [
{
type: 'input',
name: 'address.city',
+ initialValue: 'Berlin',
props: {
required: false,
- value: 'Berlin',
type: 'text',
label: 'Ort',
placeholder: 'City...',
diff --git a/src/storiesWrappers/FormWrapper.js b/src/storiesWrappers/FormWrapper.js
index eacf018fc..d153cf587 100644
--- a/src/storiesWrappers/FormWrapper.js
+++ b/src/storiesWrappers/FormWrapper.js
@@ -1,5 +1,3 @@
-export default function FormWrapper() {
- return {
- template: '
',
- };
-}
+export default () => ({
+ template: '
',
+});
diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss
index fa5892682..6a578b132 100644
--- a/src/styles/_variables.scss
+++ b/src/styles/_variables.scss
@@ -47,6 +47,7 @@ $max-width-paragraph-desktop: 802px;
$break-mobile: 460px !default;
$break-tablet: 768px !default;
$break-desktop: 1280px !default;
+$break-desktop-wide: 1440px !default;
/* TIMING */
$time-s: 150ms;
diff --git a/src/styles/inputs.scss b/src/styles/inputs.scss
index 74ce0f038..2ddeac383 100644
--- a/src/styles/inputs.scss
+++ b/src/styles/inputs.scss
@@ -296,10 +296,12 @@ $inputFilledBackground: #e8eaee;
padding-right: $inline-l;
}
- #{$f}--hasTooltip &,
- &[type=email],
- &[type=number],
- &[type=text] {
+ #{$f}--hasControl &[type="email"],
+ #{$f}--hasControl &[type="number"],
+ #{$f}--hasControl &[type="text"],
+ #{$f}--hasControl &[type="password"],
+ #{$f}--hasControl &[type="date"],
+ #{$f}--hasControl &[type="url"] {
padding-right: $inline-m + $iconDimension;
}
@@ -393,7 +395,6 @@ $inputFilledBackground: #e8eaee;
}
&--select {
- cursor: pointer;
&:after {
background-image: url('~hd-blocks/assets/icons/ic-arrow_down.svg');
}
diff --git a/src/styles/main.scss b/src/styles/main.scss
index de7933eb0..eaec7fccc 100644
--- a/src/styles/main.scss
+++ b/src/styles/main.scss
@@ -7,7 +7,7 @@
}
body {
- font-family: $font-primary;
+ @include font('body');
color: $cello;
}
diff --git a/tests/unit/components/HdTable.spec.js b/tests/unit/components/HdTable.spec.js
new file mode 100644
index 000000000..0198a93bb
--- /dev/null
+++ b/tests/unit/components/HdTable.spec.js
@@ -0,0 +1,135 @@
+import { mount } from '@vue/test-utils';
+import HdTable from '@/components/HdTable.vue';
+
+const headerData = ['a', 'b', 'c'];
+const bodyData = [
+ [1, 2, 3],
+ [4, 5, 6],
+];
+
+describe('HdTable', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = mount(HdTable, {
+ propsData: {
+ header: headerData,
+ body: bodyData,
+ },
+ });
+ });
+
+ test('renders component', () => {
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ test('creates a ', () => {
+ const table = wrapper.find('table');
+
+ expect(table.exists()).toBe(true);
+ });
+
+ describe('props', () => {
+ describe('fixed', () => {
+ test('only accepts Boolean', () => {
+ const { fixed } = wrapper.vm.$options.props;
+
+ expect(fixed.type).toBe(Boolean);
+ });
+
+ test('defaults to false', () => {
+ const { fixed } = wrapper.props();
+
+ expect(fixed).toBe(false);
+ });
+ });
+
+ describe('noWrap', () => {
+ test('only accepts Boolean', () => {
+ const { noWrap } = wrapper.vm.$options.props;
+
+ expect(noWrap.type).toBe(Boolean);
+ });
+
+ test('defaults to false', () => {
+ const { noWrap } = wrapper.props();
+
+ expect(noWrap).toBe(false);
+ });
+ });
+
+ describe('header', () => {
+ test('only accepts Array', () => {
+ const { header } = wrapper.vm.$options.props;
+
+ expect(header.type).toBe(Array);
+ });
+
+ test('is required', () => {
+ const { header } = wrapper.vm.$options.props;
+
+ expect(header.required).toBe(true);
+ });
+
+ test('length should be bigger than 0', () => {
+ const { header } = wrapper.vm.$options.props;
+
+ expect(header.validator([])).toBe(false);
+ expect(header.validator([1])).toBe(true);
+ });
+ });
+
+ describe('body', () => {
+ test('only accepts Array', () => {
+ const { body } = wrapper.vm.$options.props;
+
+ expect(body.type).toBe(Array);
+ });
+
+ test('is required', () => {
+ const { body } = wrapper.vm.$options.props;
+
+ expect(body.required).toBe(true);
+ });
+
+ test('length should be bigger than 0', () => {
+ const { body } = wrapper.vm.$options.props;
+
+ expect(body.validator([])).toBe(false);
+ expect(body.validator([1])).toBe(true);
+ });
+ });
+
+ describe('align', () => {
+ test('only accepts String', () => {
+ const { align } = wrapper.vm.$options.props;
+
+ expect(align.type).toBe(String);
+ });
+
+ test('defaults to center', () => {
+ const { align } = wrapper.props();
+
+ expect(align).toBe('center');
+ });
+ });
+ });
+
+ describe('methods', () => {
+ describe('isComponent', () => {
+ test('false for random object', () => {
+ const object = {};
+ const { vm } = wrapper;
+
+ expect(vm.isComponent(object)).toBe(false);
+ });
+
+ test('true for object with property component', () => {
+ const object = { component: HdTable };
+ const { vm } = wrapper;
+
+ expect(vm.isComponent(object)).toBe(true);
+ });
+ });
+ });
+});
diff --git a/tests/unit/components/HdTimeslots.spec.js b/tests/unit/components/HdTimeslots.spec.js
new file mode 100644
index 000000000..83cb4be27
--- /dev/null
+++ b/tests/unit/components/HdTimeslots.spec.js
@@ -0,0 +1,86 @@
+import { mount } from '@vue/test-utils';
+import HdTimeslots from '@/components/HdTimeslots.vue';
+import generateSlots from '@/stories/HdTimeslots.stories';
+
+describe('HdTimeslots', () => {
+ let wrapper;
+ const testTimeslots = generateSlots(400, 20);
+ // the page is shorter then the timeslots quantity
+ const testTimeslotsPerPage = Math.floor(testTimeslots.length / 2);
+ const getDisplayedTimeslots = () => wrapper.findAll('.timeslots__slot');
+ const getDisplayedTimeslotsHtml = () => wrapper.findAll('.timeslots__slot').wrappers.reduce((acc, element) => acc + element.html(), '');
+
+ beforeEach(() => {
+ wrapper = mount(HdTimeslots, {
+ propsData: {
+ timeslots: testTimeslots,
+ timeslotsPerPage: testTimeslotsPerPage,
+ },
+ });
+ });
+
+ test.skip('renders component', () => {
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ test('once the component is mounted, the first slot is selected', () => {
+ const firstTimeslot = getDisplayedTimeslots().at(0);
+
+ expect(firstTimeslot.classes()).toContain('timeslots__slot--isSelected');
+ });
+
+ test('clicking on a timeslot, the timeslot selecting method is called', () => {
+ const mockedSelectTimeslot = jest.fn();
+ const displayedTimeslots = getDisplayedTimeslots();
+ const randomTimeslot = displayedTimeslots.at(Math.floor(Math.random() * displayedTimeslots.length));
+ wrapper.setMethods({ selectTimeslot: mockedSelectTimeslot });
+ randomTimeslot.trigger('click');
+
+ expect(mockedSelectTimeslot).toHaveBeenCalled();
+ });
+
+ test('clicking on a timeslot, the timeslot is selected', () => {
+ const displayedTimeslots = getDisplayedTimeslots();
+ const randomTimeslot = displayedTimeslots.at(Math.floor(Math.random() * displayedTimeslots.length));
+ randomTimeslot.trigger('click');
+
+ expect(randomTimeslot.classes()).toContain('timeslots__slot--isSelected');
+ });
+
+ test('clicking on arrows, the page change methods are called', () => {
+ const mockedFlipPage = jest.fn();
+ wrapper.setMethods({ flipPage: mockedFlipPage });
+ wrapper.find('.arrowButton--right').trigger('click');
+
+ expect(mockedFlipPage).toHaveBeenCalledTimes(1);
+
+ wrapper.find('.arrowButton--left').trigger('click');
+
+ expect(mockedFlipPage).toHaveBeenCalledTimes(2);
+ });
+
+ test('clicking on arrows, the page is changed', () => {
+ const initialTimeslotsListContent = getDisplayedTimeslotsHtml();
+ wrapper.find('.arrowButton--right').trigger('click');
+
+ expect(getDisplayedTimeslotsHtml()).not.toBe(initialTimeslotsListContent);
+
+ wrapper.find('.arrowButton--left').trigger('click');
+
+ expect(getDisplayedTimeslotsHtml()).toBe(initialTimeslotsListContent);
+ });
+
+ test('slots are displayed in the proper quantity', () => {
+ const { timeslots } = wrapper.vm.$options.props;
+
+ expect(timeslots.validator(Array.from(timeslots))).toBe(true);
+ expect(getDisplayedTimeslots().length).toBe(testTimeslotsPerPage);
+
+ // the page is now wider then the timeslots quantity
+ wrapper.setProps({ timeslotsPerPage: testTimeslots.length * 2 });
+
+ expect(getDisplayedTimeslots().length).toBe(testTimeslots.length);
+ // data validity check
+ expect(testTimeslots.length).toBeGreaterThan(0);
+ });
+});
diff --git a/tests/unit/components/HdToast.spec.js b/tests/unit/components/HdToast.spec.js
new file mode 100644
index 000000000..261686e2b
--- /dev/null
+++ b/tests/unit/components/HdToast.spec.js
@@ -0,0 +1,257 @@
+import { mount } from '@vue/test-utils';
+import HdToast from '@/components/HdToast.vue';
+
+describe('HdToast', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = mount(HdToast);
+ });
+
+ test('renders component', () => {
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ test('renders component closed', () => {
+ const data = wrapper.vm.$data;
+
+ expect(data.isOpen).toBe(false);
+ });
+
+ test("'primaryLabel' text binds to primary ", () => {
+ const text = 'random-text';
+
+ wrapper.setProps({ primaryLabel: text });
+
+ const button = wrapper.find('.toast__control--primary');
+
+ expect(wrapper.html()).toMatchSnapshot();
+ expect(button.text()).toBe(text);
+ });
+
+ test("primary calls 'primaryClick' when clicked", () => {
+ const text = 'random-text';
+ const primaryClickMock = jest.fn();
+
+ wrapper.setProps({ primaryLabel: text });
+ wrapper.setMethods({ primaryClick: primaryClickMock });
+
+ const button = wrapper.find('.toast__control--primary');
+
+ button.trigger('click');
+
+ expect(primaryClickMock).toHaveBeenCalledTimes(1);
+ });
+
+ test("'secondaryLabel' text binds to secondary ", () => {
+ const text = 'random-text';
+ wrapper.setProps({ secondaryLabel: text });
+
+ const button = wrapper.find('.toast__control--secondary');
+
+ expect(wrapper.html()).toMatchSnapshot();
+ expect(button.text()).toBe(text);
+ });
+
+ test("secondary calls 'secondaryClick' when clicked", () => {
+ const text = 'random-text';
+ const secondaryClickMock = jest.fn();
+
+ wrapper.setProps({ secondaryLabel: text });
+ wrapper.setMethods({ secondaryClick: secondaryClickMock });
+
+ const button = wrapper.find('.toast__control--secondary');
+
+ button.trigger('click');
+
+ expect(secondaryClickMock).toHaveBeenCalledTimes(1);
+ });
+
+ test('renders default slot', () => {
+ const innerWrapper = mount(HdToast, {
+ slots: {
+ default: '
',
+ },
+ });
+ const slot = innerWrapper.find('#slot');
+
+ expect(innerWrapper.html()).toMatchSnapshot();
+ expect(slot.exists()).toBe(true);
+ });
+
+ test("renders buttons when 'hasLabels'", () => {
+ const className = '.toast__controls';
+
+ expect(wrapper.find(className).exists()).toBe(false);
+ expect(wrapper.html()).toMatchSnapshot();
+
+ wrapper.setProps({ primaryLabel: 'random-text' });
+
+ expect(wrapper.find(className).exists()).toBe(true);
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ test("renders button primary when 'primaryLabel'", () => {
+ wrapper.setProps({ primaryLabel: 'random-text' });
+
+ const className = '.toast__control';
+
+ expect(wrapper.find(`${className}--primary`).exists()).toBe(true);
+ expect(wrapper.find(`${className}--secondary`).exists()).toBe(false);
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ test("renders button secondary when 'secondaryLabel'", () => {
+ wrapper.setProps({ secondaryLabel: 'random-text' });
+
+ const className = '.toast__control';
+
+ expect(wrapper.find(`${className}--primary`).exists()).toBe(false);
+ expect(wrapper.find(`${className}--secondary`).exists()).toBe(true);
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ describe('props', () => {
+ describe('primaryLabel', () => {
+ test('defaults to undefined', () => {
+ const { primaryLabel } = wrapper.props();
+
+ expect(primaryLabel).toBeUndefined();
+ });
+
+ test('only accepts String', () => {
+ const { primaryLabel } = wrapper.vm.$options.props;
+
+ expect(primaryLabel.type).toBe(String);
+ });
+ });
+
+ describe('secondaryLabel', () => {
+ test('defaults to undefined', () => {
+ const { secondaryLabel } = wrapper.props();
+
+ expect(secondaryLabel).toBeUndefined();
+ });
+
+ test('only accepts String', () => {
+ const { secondaryLabel } = wrapper.vm.$options.props;
+
+ expect(secondaryLabel.type).toBe(String);
+ });
+ });
+ });
+
+ describe('computed', () => {
+ describe('hasLabels', () => {
+ test('defaults to false', () => {
+ const { vm } = wrapper;
+
+ expect(vm.hasLabels).toBe(false);
+ });
+
+ test('true when primaryLabel exists', () => {
+ const { vm } = wrapper;
+
+ wrapper.setProps({ primaryLabel: 'random-text' });
+
+ expect(vm.hasLabels).toBe(true);
+ });
+
+ test('true when secondaryLabel exists', () => {
+ const { vm } = wrapper;
+
+ wrapper.setProps({ secondaryLabel: 'random-text' });
+
+ expect(vm.hasLabels).toBe(true);
+ });
+ });
+
+ describe('computedClasses', () => {
+ test("'toast--isOpen' when isOpen is true", () => {
+ const { vm } = wrapper;
+ const className = 'toast--isOpen';
+
+ expect(vm.computedClasses[className]).toBe(false);
+ expect(wrapper.html()).toMatchSnapshot();
+
+ wrapper.setData({ isOpen: true });
+
+ expect(vm.computedClasses[className]).toBe(true);
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ test("'toast--isClosing' when isClosing is true", () => {
+ const { vm } = wrapper;
+ const className = 'toast--isClosing';
+
+ expect(vm.computedClasses[className]).toBe(false);
+ expect(wrapper.html()).toMatchSnapshot();
+
+ wrapper.setData({ isClosing: true });
+
+ expect(vm.computedClasses[className]).toBe(true);
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+ });
+ });
+
+ describe('methods', () => {
+ describe('open', () => {
+ test('sets isOpen to true', () => {
+ const { vm } = wrapper;
+
+ expect(vm.isOpen).toBe(false);
+
+ vm.open();
+
+ expect(vm.isOpen).toBe(true);
+ });
+ });
+
+ describe('primaryClick', () => {
+ test("emits 'primaryClick' event", () => {
+ wrapper.vm.primaryClick();
+
+ expect(wrapper.emitted().primaryClick).toBeTruthy();
+ expect(wrapper.emitted().primaryClick.length).toBe(1);
+ });
+ });
+
+ describe('secondaryClick', () => {
+ test("emits 'secondaryClick' event and call 'close'", () => {
+ const closeMock = jest.fn();
+
+ wrapper.setMethods({ close: closeMock });
+ wrapper.vm.secondaryClick();
+
+ expect(wrapper.emitted().secondaryClick).toBeTruthy();
+ expect(wrapper.emitted().secondaryClick.length).toBe(1);
+
+ expect(closeMock).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ describe('close', () => {
+ test("sets 'isClosing' to true before timers then sets 'isOpen' and 'isClosing' to false and emits 'close'", () => {
+ jest.useFakeTimers();
+
+ const { vm } = wrapper;
+
+ vm.close();
+
+ expect(vm.isClosing).toBe(true);
+
+ jest.runAllTimers();
+
+ expect(setTimeout).toHaveBeenCalledTimes(1);
+ expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 500);
+
+ expect(vm.isOpen).toBe(false);
+ expect(vm.isClosing).toBe(false);
+
+ expect(wrapper.emitted().close).toBeTruthy();
+ expect(wrapper.emitted().close.length).toBe(1);
+ });
+ });
+ });
+});
diff --git a/tests/unit/components/__snapshots__/HdTable.spec.js.snap b/tests/unit/components/__snapshots__/HdTable.spec.js.snap
new file mode 100644
index 000000000..c6526ca7b
--- /dev/null
+++ b/tests/unit/components/__snapshots__/HdTable.spec.js.snap
@@ -0,0 +1,25 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`HdTable renders component 1`] = `
+
+
+
+ a
+ b
+ c
+
+
+
+
+ 1
+ 2
+ 3
+
+
+ 4
+ 5
+ 6
+
+
+
+`;
diff --git a/tests/unit/components/__snapshots__/HdTimeslots.spec.js.snap b/tests/unit/components/__snapshots__/HdTimeslots.spec.js.snap
new file mode 100644
index 000000000..743e1dde1
--- /dev/null
+++ b/tests/unit/components/__snapshots__/HdTimeslots.spec.js.snap
@@ -0,0 +1,38 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`HdTimeslots renders component 1`] = `
+
+
+
+
+ 10:20
+
+ 10:40
+
+ 11:00
+
+ 11:20
+
+ 11:40
+
+ 12:00
+
+ 12:20
+
+ 12:40
+
+ 13:00
+
+ 13:20
+
+
+
+ NOT_AVAILABLE
+
+
+ AVAILABLE
+
+
+
+
+`;
diff --git a/tests/unit/components/__snapshots__/HdToast.spec.js.snap b/tests/unit/components/__snapshots__/HdToast.spec.js.snap
new file mode 100644
index 000000000..e416ae3d5
--- /dev/null
+++ b/tests/unit/components/__snapshots__/HdToast.spec.js.snap
@@ -0,0 +1,94 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`HdToast 'primaryLabel' text binds to primary 1`] = `
+
+`;
+
+exports[`HdToast 'secondaryLabel' text binds to secondary 1`] = `
+
+`;
+
+exports[`HdToast computed computedClasses 'toast--isClosing' when isClosing is true 1`] = `
+
+`;
+
+exports[`HdToast computed computedClasses 'toast--isClosing' when isClosing is true 2`] = `
+
+`;
+
+exports[`HdToast computed computedClasses 'toast--isOpen' when isOpen is true 1`] = `
+
+`;
+
+exports[`HdToast computed computedClasses 'toast--isOpen' when isOpen is true 2`] = `
+
+`;
+
+exports[`HdToast renders button primary when 'primaryLabel' 1`] = `
+
+`;
+
+exports[`HdToast renders button secondary when 'secondaryLabel' 1`] = `
+
+`;
+
+exports[`HdToast renders buttons when 'hasLabels' 1`] = `
+
+`;
+
+exports[`HdToast renders buttons when 'hasLabels' 2`] = `
+
+`;
+
+exports[`HdToast renders component 1`] = `
+
+`;
+
+exports[`HdToast renders default slot 1`] = `
+
+`;
diff --git a/tests/unit/components/buttons/HdArrowButton.spec.js b/tests/unit/components/buttons/HdArrowButton.spec.js
new file mode 100644
index 000000000..de7f3dbbc
--- /dev/null
+++ b/tests/unit/components/buttons/HdArrowButton.spec.js
@@ -0,0 +1,100 @@
+import { mount } from '@vue/test-utils';
+import HdArrowButton from '@/components/buttons/HdArrowButton.vue';
+
+describe('HdArrowButton', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = mount(HdArrowButton);
+ });
+
+ test('renders component', () => {
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ test('creates a ', () => {
+ const button = wrapper.find('button');
+
+ expect(button.exists()).toBe(true);
+ });
+
+ test(" has 'arrowButton' class", () => {
+ const button = wrapper.find('button');
+
+ expect(button.classes()).toContain('arrowButton');
+ });
+
+ // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=654072
+ test(" has autocomplete='off' attribute", () => {
+ const button = wrapper.find('button');
+ const { autocomplete } = button.attributes();
+
+ expect(autocomplete).toBe('off');
+ });
+
+ describe('props', () => {
+ describe('disabled', () => {
+ test('defaults to false', () => {
+ const { disabled } = wrapper.props();
+
+ expect(disabled).toBe(false);
+ });
+
+ test('only accepts Boolean', () => {
+ const { disabled } = wrapper.vm.$options.props;
+
+ expect(disabled.type).toBe(Boolean);
+ });
+
+ test('disables button when true', () => {
+ const button = wrapper.find('button');
+
+ expect(button.attributes().disabled).toBeUndefined();
+ expect(wrapper.html()).toMatchSnapshot();
+
+ wrapper.setProps({ disabled: true });
+
+ expect(button.attributes().disabled).toBeTruthy();
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+ });
+
+ describe('direction', () => {
+ test('defaults to right', () => {
+ const { direction } = wrapper.props();
+
+ expect(direction).toBe('right');
+ });
+
+ test('only accepts String', () => {
+ const { direction } = wrapper.vm.$options.props;
+
+ expect(direction.type).toBe(String);
+ });
+
+ test("only accepts 'right' and 'left'", () => {
+ const { direction } = wrapper.vm.$options.props;
+
+ expect(direction.validator('right')).toBe(true);
+ expect(direction.validator('left')).toBe(true);
+ expect(direction.validator('top')).toBe(false);
+ });
+ });
+
+ describe('computed', () => {
+ describe('computedClasses', () => {
+ test('direction class based on direction prop', () => {
+ const { vm } = wrapper;
+
+ wrapper.setProps({ direction: 'right' });
+ expect(vm.computedClasses).toBe('arrowButton--right');
+ expect(wrapper.html()).toMatchSnapshot();
+
+ wrapper.setProps({ direction: 'left' });
+ expect(vm.computedClasses).toBe('arrowButton--left');
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+ });
+ });
+ });
+});
diff --git a/tests/unit/components/buttons/HdLoaderButton.spec.js b/tests/unit/components/buttons/HdLoaderButton.spec.js
new file mode 100644
index 000000000..b162a97c2
--- /dev/null
+++ b/tests/unit/components/buttons/HdLoaderButton.spec.js
@@ -0,0 +1,38 @@
+import { shallowMount } from '@vue/test-utils';
+import HdLoaderButton from '@/components/buttons/HdLoaderButton.vue';
+
+// FIXME: JSDOM API issue.
+// querySelectorAll('paths') returns SVGElement instead of the DOM API SVGPathElement
+SVGElement.prototype.getTotalLength = () => 42;
+
+describe('HdLoaderButton', () => {
+ let wrapper;
+ const testLabel = 'Lorem';
+ const mockedClick = jest.fn();
+
+ beforeEach(() => {
+ wrapper = shallowMount(HdLoaderButton, {
+ propsData: {
+ label: testLabel,
+ },
+ });
+
+ wrapper.setMethods({ clicked: mockedClick });
+ });
+
+ test('the component is rendered', () => {
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ test('the label is displayed', () => {
+ const labelContainer = wrapper.find('.loaderButton__button__label');
+ expect(labelContainer.exists()).toBeTruthy();
+ expect(labelContainer.text()).toEqual(testLabel);
+ });
+
+ test('clicking on the button, calls the proper method', () => {
+ wrapper.find('.btn--primary').trigger('click');
+
+ expect(mockedClick).toHaveBeenCalled();
+ });
+});
diff --git a/tests/unit/components/buttons/HdRadioButton.spec.js b/tests/unit/components/buttons/HdRadioButton.spec.js
new file mode 100644
index 000000000..2a9841920
--- /dev/null
+++ b/tests/unit/components/buttons/HdRadioButton.spec.js
@@ -0,0 +1,47 @@
+import { mount } from '@vue/test-utils';
+import HdRadioButton from '@/components/buttons/HdRadioButton.vue';
+
+const desktopIcon = 'desktop-icon-url';
+const desktopIconHover = 'desktop-icon-hover-url';
+const mobileIcon = 'mobile-icon-url';
+
+describe('HdRadioButton', () => {
+ let wrapper;
+
+ const name = 'lorem';
+ const value = 'ipsum';
+ const label = 'dolor';
+ const mockedSelect = jest.fn();
+
+ beforeEach(() => {
+ wrapper = mount(HdRadioButton, {
+ propsData: {
+ name,
+ value,
+ label,
+ desktopIcon,
+ desktopIconHover,
+ mobileIcon,
+ },
+ });
+
+ wrapper.setMethods({ select: mockedSelect });
+ });
+
+ test('the component is rendered', () => {
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ test('on change, the proper method is invoked', () => {
+ const input = wrapper.find('input');
+
+ expect(input.attributes().name).toBe(name);
+ expect(input.attributes().value).toBe(value);
+
+ expect(wrapper.find('label').text()).toBe(label);
+
+ input.trigger('change');
+
+ expect(mockedSelect).toHaveBeenCalled();
+ });
+});
diff --git a/tests/unit/components/buttons/__snapshots__/HdArrowButton.spec.js.snap b/tests/unit/components/buttons/__snapshots__/HdArrowButton.spec.js.snap
new file mode 100644
index 000000000..57e467e38
--- /dev/null
+++ b/tests/unit/components/buttons/__snapshots__/HdArrowButton.spec.js.snap
@@ -0,0 +1,11 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`HdArrowButton props computed computedClasses direction class based on direction prop 1`] = ` `;
+
+exports[`HdArrowButton props computed computedClasses direction class based on direction prop 2`] = ` `;
+
+exports[`HdArrowButton props disabled disables button when true 1`] = ` `;
+
+exports[`HdArrowButton props disabled disables button when true 2`] = ` `;
+
+exports[`HdArrowButton renders component 1`] = ` `;
diff --git a/tests/unit/components/buttons/__snapshots__/HdLoaderButton.spec.js.snap b/tests/unit/components/buttons/__snapshots__/HdLoaderButton.spec.js.snap
new file mode 100644
index 000000000..540842ec1
--- /dev/null
+++ b/tests/unit/components/buttons/__snapshots__/HdLoaderButton.spec.js.snap
@@ -0,0 +1,19 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`HdLoaderButton the component is rendered 1`] = `
+
+`;
diff --git a/tests/unit/components/buttons/__snapshots__/HdRadioButton.spec.js.snap b/tests/unit/components/buttons/__snapshots__/HdRadioButton.spec.js.snap
new file mode 100644
index 000000000..bb8acbc82
--- /dev/null
+++ b/tests/unit/components/buttons/__snapshots__/HdRadioButton.spec.js.snap
@@ -0,0 +1,13 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`HdRadioButton the component is rendered 1`] = `
+
+`;
diff --git a/tests/unit/components/tooltip/HdTooltip.spec.js b/tests/unit/components/tooltip/HdTooltip.spec.js
new file mode 100644
index 000000000..0f9639eb8
--- /dev/null
+++ b/tests/unit/components/tooltip/HdTooltip.spec.js
@@ -0,0 +1,18 @@
+import { shallowMount } from '@vue/test-utils';
+import HdTooltip from '@/components/tooltip/HdTooltip.vue';
+
+describe('HdTooltip', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = shallowMount(HdTooltip);
+ });
+
+ test('the component is rendered', () => {
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ test('the window tooltip element is set properly', () => {
+ expect(window.tooltipElement).toEqual(wrapper.element);
+ });
+});
diff --git a/tests/unit/components/tooltip/HdTooltipped.spec.js b/tests/unit/components/tooltip/HdTooltipped.spec.js
new file mode 100644
index 000000000..2c0736941
--- /dev/null
+++ b/tests/unit/components/tooltip/HdTooltipped.spec.js
@@ -0,0 +1,35 @@
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import HDTooltipped from '@/components/tooltip/HdTooltipped.vue';
+import HdInput from '@/components/form/HdInput.vue';
+import tooltipDirective from '@/components/tooltip/tooltipDirective';
+
+// required to properly solve HDTooltipped
+Vue.directive('hd-tooltip', tooltipDirective);
+
+describe('HDTooltipped', () => {
+ let emptyWrapper;
+ let wrapper;
+
+ beforeEach(() => {
+ emptyWrapper = shallowMount(HDTooltipped);
+
+ wrapper = shallowMount(HDTooltipped, {
+ propsData: {
+ component: HdInput,
+ value: 'Lorem ipsum',
+ direction: 'left',
+ props: {
+ label: 'dolor sit ammet',
+ type: 'text',
+ name: 'testInput',
+ },
+ },
+ });
+ });
+
+ test('the component is rendered', () => {
+ expect(emptyWrapper.html()).toMatchSnapshot();
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+});
diff --git a/tests/unit/components/tooltip/__snapshots__/HdTooltip.spec.js.snap b/tests/unit/components/tooltip/__snapshots__/HdTooltip.spec.js.snap
new file mode 100644
index 000000000..6435bf403
--- /dev/null
+++ b/tests/unit/components/tooltip/__snapshots__/HdTooltip.spec.js.snap
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`HdTooltip the component is rendered 1`] = ` `;
diff --git a/tests/unit/components/tooltip/__snapshots__/HdTooltipped.spec.js.snap b/tests/unit/components/tooltip/__snapshots__/HdTooltipped.spec.js.snap
new file mode 100644
index 000000000..9d6d0f1b5
--- /dev/null
+++ b/tests/unit/components/tooltip/__snapshots__/HdTooltipped.spec.js.snap
@@ -0,0 +1,11 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`HDTooltipped the component is rendered 1`] = ` `;
+
+exports[`HDTooltipped the component is rendered 2`] = `
+
+ dolor sit ammet
+
+
+
+`;