diff --git a/components/accordion/Accordion.d.ts b/components/accordion/Accordion.d.ts new file mode 100755 index 000000000..f70354ce2 --- /dev/null +++ b/components/accordion/Accordion.d.ts @@ -0,0 +1,90 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface AccordionTabOpenEvent { + /** + * Browser mouse event. + * @type {MouseEvent} + */ + originalEvent: MouseEvent; + /** + * Opened tab index. + */ + index: number; +} + +/** + * @extends {AccordionTabOpenEvent} + */ +export interface AccordionTabCloseEvent extends AccordionTabOpenEvent { } + +export interface AccordionProps { + /** + * When enabled, multiple tabs can be activated at the same time. + */ + multiple?: boolean | undefined; + /** + * Index of the active tab or an array of indexes in multiple mode. + */ + activeIndex?: number | number[] | undefined; + /** + * When enabled, hidden tabs are not rendered at all. Defaults to false that hides tabs with css. + */ + lazy?: boolean | undefined; + /** + * Icon of a collapsed tab. + */ + expandIcon?: string | undefined; + /** + * Icon of an expanded tab. + */ + collapseIcon?: string | undefined; +} + +export interface AccordionSlots { + /** + * Default slot to detect AccordionTab components. + */ + default: () => VNode[]; +} + +export declare type AccordionEmits = { + /** + * Emitted when the active tab changes. + * @param {number | undefined} value - Index of new active tab. + */ + 'update:activeIndex': (value: number | undefined) => void; + /** + * Callback to invoke when a tab gets expanded. + * @param {AccordionTabOpenEvent} event - Custom tab open event. + */ + 'tab-open': (event: AccordionTabOpenEvent) => void; + /** + * Callback to invoke when an active tab is collapsed by clicking on the header. + * @param {AccordionTabCloseEvent} event - Custom tab close event. + */ + 'tab-close': (event: AccordionTabCloseEvent) => void; +} + +declare class Accordion extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Accordion: GlobalComponentConstructor + } +} + +/** + * + * Accordion groups a collection of contents in tabs. + * + * Helper Components: + * + * - AccordionTab + * + * Demos: + * + * - [Accordion](https://www.primefaces.org/primevue/showcase/#/accordion) + * + */ +export default Accordion; diff --git a/components/accordion/Accordion.spec.js b/components/accordion/Accordion.spec.js new file mode 100644 index 000000000..cc5ecac18 --- /dev/null +++ b/components/accordion/Accordion.spec.js @@ -0,0 +1,50 @@ +import { mount } from '@vue/test-utils'; +import Accordion from '@/components/accordion/Accordion.vue'; +import AccordionTab from '@/components/accordiontab/AccordionTab.vue'; + +describe('Accordion.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Accordion, { + global: { + components: { + AccordionTab + } + }, + slots: { + default: ` + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+
+ +

Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi + architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione + voluptatem sequi nesciunt. Consectetur, adipisci velit, sed quia non numquam eius modi.

+
+ +

At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati + cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. + Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus.

+
` + } + }); + }); + + it('should Accordion and AccordionTab component exist', () => { + expect(wrapper.find('.p-accordion.p-component').exists()).toBe(true); + expect(wrapper.find('.p-accordion-tab').exists()).toBe(true); + expect(wrapper.findAll('.p-accordion-tab').length).toBe(3); + }); + + it('should activeIndex change', async() => { + await wrapper.setProps({ activeIndex: 1 }); + + const allTabs = wrapper.findAll('.p-accordion-tab'); + + expect(allTabs[0].classes()).not.toContain('p-accordion-tab-active'); + expect(allTabs[1].classes()).toContain('p-accordion-tab-active'); + }); +}); \ No newline at end of file diff --git a/components/accordion/Accordion.vue b/components/accordion/Accordion.vue new file mode 100755 index 000000000..19f6d97a1 --- /dev/null +++ b/components/accordion/Accordion.vue @@ -0,0 +1,169 @@ + + + + + diff --git a/components/accordion/package.json b/components/accordion/package.json new file mode 100644 index 000000000..adf2abe3d --- /dev/null +++ b/components/accordion/package.json @@ -0,0 +1,9 @@ +{ + "main": "./accordion.cjs.js", + "module": "./accordion.esm.js", + "unpkg": "./accordion.min.js", + "types": "./Accordion.d.ts", + "browser": { + "./sfc": "./Accordion.vue" + } +} \ No newline at end of file diff --git a/components/accordiontab/AccordionTab.d.ts b/components/accordiontab/AccordionTab.d.ts new file mode 100755 index 000000000..aee6fb00d --- /dev/null +++ b/components/accordiontab/AccordionTab.d.ts @@ -0,0 +1,45 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface AccordionTabProps { + /** + * Orientation of tab headers. + */ + header?: string | undefined; + /** + * Whether the tab is disabled. + */ + disabled?: boolean | undefined; +} + +export interface AccordionTabSlots { + /** + * Default slot for content. + */ + default: () => VNode[]; + /** + * Custom content for the title section of a panel is defined using the header template. + */ + header: () => VNode[]; +} + +export declare type AccordionTabEmits = { } + +declare class AccordionTab extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + AccordionTab: GlobalComponentConstructor + } +} + +/** + * + * AccordionTab is a helper component for Accordion. + * + * Demos: + * + * - [Accordion](https://www.primefaces.org/primevue/showcase/#/accordion) + * + */ +export default AccordionTab; diff --git a/components/accordiontab/AccordionTab.spec.js b/components/accordiontab/AccordionTab.spec.js new file mode 100644 index 000000000..65f46426e --- /dev/null +++ b/components/accordiontab/AccordionTab.spec.js @@ -0,0 +1,14 @@ +import { mount } from '@vue/test-utils'; +import AccordionTab from '@/components/accordiontab/AccordionTab.vue'; + +describe('AccordionTab.vue', () => { + it('should exists', () => { + const wrapper = mount(AccordionTab, { + slots: { + default: '

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do

' + } + }); + + expect(wrapper.text()).toBe('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do'); + }); +}); \ No newline at end of file diff --git a/components/accordiontab/AccordionTab.vue b/components/accordiontab/AccordionTab.vue new file mode 100755 index 000000000..6bcdfa7ed --- /dev/null +++ b/components/accordiontab/AccordionTab.vue @@ -0,0 +1,13 @@ + + + diff --git a/components/accordiontab/package.json b/components/accordiontab/package.json new file mode 100644 index 000000000..415127406 --- /dev/null +++ b/components/accordiontab/package.json @@ -0,0 +1,9 @@ +{ + "main": "./accordiontab.cjs.js", + "module": "./accordiontab.esm.js", + "unpkg": "./accordiontab.min.js", + "types": "./AccordionTab.d.ts", + "browser": { + "./sfc": "./AccordionTab.vue" + } +} \ No newline at end of file diff --git a/components/autocomplete/AutoComplete.d.ts b/components/autocomplete/AutoComplete.d.ts new file mode 100755 index 000000000..3d28711dd --- /dev/null +++ b/components/autocomplete/AutoComplete.d.ts @@ -0,0 +1,447 @@ +import { HTMLAttributes, InputHTMLAttributes, VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; +import { VirtualScrollerProps, VirtualScrollerItemOptions } from '../virtualscroller'; + +type AutoCompleteFieldType = string | ((data: any) => string) | undefined; + +type AutoCompleteOptionLabelType = string | ((data: any) => string) | undefined; + +type AutoCompleteOptionDisabledType = string | ((data: any) => boolean) | undefined; + +type AutoCompleteOptionChildrenType = string | ((data: any) => any[]) | undefined; + +type AutoCompleteDropdownMode = 'blank' | 'current' | undefined; + +type AutoCompleteAppendTo = 'body' | 'self' | string | undefined | HTMLElement; + +export interface AutoCompleteChangeEvent { + /** + * Browser event. + */ + originalEvent: Event; + /** + * Selected option value + */ + value: any; +} + +export interface AutoCompleteItemSelectEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Selected item + */ + value: any; +} + +/** + * @extends AutoCompleteItemSelectEvent + */ +export interface AutoCompleteItemUnselectEvent extends AutoCompleteItemSelectEvent { } + +export interface AutoCompleteDropdownClickEvent { + /** + * Browser mouse event + */ + originalEvent: MouseEvent; + /** + * Current value of the input field + */ + query: string; +} + +export interface AutoCompleteCompleteEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Value to search with + */ + query: string; +} + +export interface AutoCompleteProps { + /** + * Value of the component. + */ + modelValue?: any; + /** + * An array of suggestions to display. + */ + suggestions?: any[]; + /** + * @deprecated since v3.16.0. Use 'optionLabel' property instead. + * Property name or getter function of a suggested object to resolve and display. + * @type {AutoCompleteFieldType} + */ + field?: AutoCompleteFieldType; + /** + * Property name or getter function to use as the label of an option. + * @see AutoCompleteOptionLabelType + */ + optionLabel?: AutoCompleteOptionLabelType; + /** + * Property name or getter function to use as the disabled flag of an option, defaults to false when not defined. + * @see AutoCompleteOptionDisabledType + */ + optionDisabled?: AutoCompleteOptionDisabledType; + /** + * Property name or getter function to use as the label of an option group. + * @see AutoCompleteOptionLabelType + */ + optionGroupLabel?: AutoCompleteOptionLabelType; + /** + * Property name or getter function that refers to the children options of option group. + * @see AutoCompleteOptionChildrenType + */ + optionGroupChildren?: AutoCompleteOptionChildrenType; + /** + * Maximum height of the suggestions panel. + * Default value is '200px'. + */ + scrollHeight?: string | undefined; + /** + * Displays a button next to the input field when enabled. + */ + dropdown?: boolean | undefined; + /** + * Specifies the behavior dropdown button. Default 'blank' mode sends an empty string and 'current' mode sends the input value. + * @see AutoCompleteDropdownMode + * Default value is 'blank'. + */ + dropdownMode?: AutoCompleteDropdownMode; + /** + * @deprecated since v3.16.0 + * Highlights automatically the first item of the dropdown to be selected. + */ + autoHighlight?: boolean | undefined; + /** + * Specifies if multiple values can be selected. + */ + multiple?: boolean | undefined; + /** + * Default text to display when no option is selected. + */ + placeholder?: string | undefined; + /** + * When present, it specifies that the component should be disabled. + */ + disabled?: boolean | undefined; + /** + * A property to uniquely identify an option. + */ + dataKey?: string | undefined; + /** + * Minimum number of characters to initiate a search. + * Default value is 1. + */ + minLength?: number | undefined; + /** + * Delay between keystrokes to wait before sending a query. + * Default value is 300. + */ + delay?: number | undefined; + /** + * A valid query selector or an HTMLElement to specify where the overlay gets attached. + * Special keywords are 'body' for document body and 'self' for the element itself. + * @see AutoCompleteAppendTo + * Default value is body. + */ + appendTo?: AutoCompleteAppendTo; + /** + * When present, autocomplete clears the manual input if it does not match of the suggestions to force only accepting values from the suggestions. + */ + forceSelection?: boolean; + /** + * Whether to run a query when input receives focus. + */ + completeOnFocus?: boolean | undefined; + /** + * Identifier of the underlying input element. + */ + inputId?: string | undefined; + /** + * Inline style of the input field. + */ + inputStyle?: any; + /** + * Style class of the input field. + */ + inputClass?: any; + /** + * Uses to pass all properties of the HTMLInputElement to the focusable input element inside the component. + */ + inputProps?: InputHTMLAttributes | undefined; + /** + * Inline style of the overlay panel. + */ + panelStyle?: any; + /** + * Style class of the overlay panel. + */ + panelClass?: any; + /** + * Uses to pass all properties of the HTMLDivElement to the overlay panel inside the component. + */ + panelProps?: HTMLAttributes | undefined; + /** + * Icon to display in loading state. + * Default value is 'pi pi-spinner pi-spin'. + */ + loadingIcon?: string | undefined; + /** + * Whether to use the virtualScroller feature. The properties of VirtualScroller component can be used like an object in it. + * @see VirtualScroller.VirtualScrollerProps + */ + virtualScrollerOptions?: VirtualScrollerProps; + /** + * Whether to focus on the first visible or selected element when the overlay panel is shown. + * Default value is true. + */ + autoOptionFocus?: boolean | undefined; + /** + * Locale to use in searching. The default locale is the host environment's current locale. + */ + searchLocale?: string | undefined; + /** + * Text to be displayed in hidden accessible field when filtering returns any results. Defaults to value from PrimeVue locale configuration. + * Default value is '{0} results are available'. + */ + searchMessage?: string | undefined; + /** + * Text to be displayed in hidden accessible field when options are selected. Defaults to value from PrimeVue locale configuration. + * Default value is '{0} items selected'. + */ + selectionMessage?: string | undefined; + /** + * Text to be displayed in hidden accessible field when any option is not selected. Defaults to value from PrimeVue locale configuration. + * Default value is 'No selected item'. + */ + emptySelectionMessage?: string | undefined; + /** + * Text to display when filtering does not return any results. Defaults to value from PrimeVue locale configuration. + * Default value is 'No results found'. + */ + emptySearchMessage?: string | undefined; + /** + * Index of the element in tabbing order. + */ + tabindex?: number | string | undefined; + /** + * Defines a string value that labels an interactive element. + */ + "aria-label"?: string | undefined; + /** + * Identifier of the underlying input element. + */ + "aria-labelledby"?: string | undefined; +} + +export interface AutoCompleteSlots { + /** + * Custom chip template. + * @param {Object} scope - chip slot's params. + */ + chip: (scope: { + /** + * A value in the selection + */ + value: any; + }) => VNode[]; + /** + * Custom header template of panel. + * @param {Object} scope - header slot's params. + */ + header: (scope: { + /** + * Value of the component + */ + value: any; + /** + * Displayed options + */ + suggestions: any[]; + }) => VNode[]; + /** + * Custom footer template of panel. + * @param {Object} scope - footer slot's params. + */ + footer: (scope: { + /** + * Value of the component + */ + value: any; + /** + * Displayed options + */ + suggestions: any[]; + }) => VNode[]; + /** + * @deprecated since v3.16.0 + * Custom content for each item. + * @param {Object} scope - item slot's params. + */ + item: (scope: { + /** + * Option instance + */ + item: any; + /** + * Index of the option + */ + index: number; + }) => VNode[]; + /** + * Custom option template. + * @param {Object} scope - option slot's params. + */ + option: (scope: { + /** + * Option instance + */ + option: any; + /** + * Index of the option + */ + index: number; + }) => VNode[]; + /** + * Custom option group template. + * @param {Object} scope - option group slot's params. + */ + optiongroup: (scope: { + /** + * @deprecated since v3.16.0. Use the 'option' instead. + * Option instance + */ + item: any; + /** + * Option instance + */ + option: any; + /** + * Index of the option + */ + index: number; + }) => VNode[]; + /** + * Custom panel template. + * @param {Object} scope - content slot's params. + */ + content: (scope: { + /** + * An array of objects to display for virtualscroller + */ + items: any; + /** + * Style class of the component + */ + styleClass: string; + /** + * Referance of the content + * @param {HTMLElement} el - Element of 'ref' property + */ + contentRef(el: any): void; + /** + * Options of the items + * @param {number} index - Rendered index + * @return {@link VirtualScroller.VirtualScrollerItemOptions} + */ + getItemOptions(index: number): VirtualScrollerItemOptions; + }) => VNode[]; + /** + * Custom loader template. + * @param {Object} scope - loader slot's params. + */ + loader: (scope: { + /** + * Options of the loader items for virtualscroller + */ + options: any[]; + }) => VNode[]; +} + +export declare type AutoCompleteEmits = { + /** + * Emitted when the value changes. + * @param {*} value - New value. + */ + 'update:modelValue': (value: any) => void; + /** + * Callback to invoke on value change. + * @param {AutoCompleteChangeEvent} event - Custom change event. + */ + 'change': (event: AutoCompleteChangeEvent) => void; + /** + * Callback to invoke when the component receives focus. + * @param {Event} event - Browser event. + */ + 'focus': (event: Event) => void; + /** + * Callback to invoke when the component loses focus. + * @param {Event} event - Browser event. + */ + 'blur': (event: Event) => void; + /** + * Callback to invoke when a suggestion is selected. + * @param {AutoCompleteItemSelectEvent} event - Custom item select event. + */ + 'item-select': (event: AutoCompleteItemSelectEvent) => void; + /** + * Callback to invoke when a selected value is removed. + * @param {AutoCompleteItemUnselectEvent} event - Custom item unselect event. + */ + 'item-unselect': (event: AutoCompleteItemUnselectEvent) => void; + /** + * Callback to invoke to when dropdown button is clicked. + * @param {AutoCompleteDropdownClickEvent} event - Custom dropdown click event. + */ + 'dropdown-click': (event: AutoCompleteDropdownClickEvent) => void; + /** + * Callback to invoke when input is cleared by the user. + */ + 'clear': () => void; + /** + * Callback to invoke to search for suggestions. + * @param {AutoCompleteCompleteEvent} event - Custom complete event. + */ + 'complete': (event: AutoCompleteCompleteEvent) => void; + /** + * Callback to invoke before the overlay is shown. + */ + 'before-show': () => void; + /** + * Callback to invoke before the overlay is hidden. + */ + 'before-hide': () => void; + /** + * Callback to invoke when the overlay is shown. + */ + 'show': () => void; + /** + * Callback to invoke when the overlay is hidden. + */ + 'hide': () => void; +} + +declare class AutoComplete extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + AutoComplete: GlobalComponentConstructor + } +} + +/** + * + * AutoComplete is an input component that provides real-time suggestions when being typed. + * + * Demos: + * + * - [AutoComplete](https://www.primefaces.org/primevue/showcase/#/autocomplete) + * + */ +export default AutoComplete; diff --git a/components/autocomplete/AutoComplete.spec.js b/components/autocomplete/AutoComplete.spec.js new file mode 100644 index 000000000..093b91942 --- /dev/null +++ b/components/autocomplete/AutoComplete.spec.js @@ -0,0 +1,58 @@ +import { mount } from '@vue/test-utils'; +import PrimeVue from '@/components/config/PrimeVue'; +import AutoComplete from './AutoComplete.vue'; + +describe('AutoComplete.vue', () => { + let wrapper; + + beforeEach(async () => { + wrapper = mount(AutoComplete, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true + } + }, + props: { + suggestions: null, + field: 'name' + }, + data() { + return { + countries: [ + {"name": "Afghanistan", "code": "AF"}, + {"name": "Bahrain", "code": "BH"}, + {"name": "Chile", "code": "CL"}, + {"name": "Denmark", "code": "DK"} + ] + } + } + }); + + await wrapper.trigger('click'); + }); + + it('should exists', () => { + expect(wrapper.find('.p-autocomplete.p-component').exists()).toBe(true); + expect(wrapper.find('.p-autocomplete-input').exists()).toBe(true); + }); + + it('search copmlete', async () => { + const event = {'target': { 'value': 'b' }}; + + wrapper.vm.onInput(event); + await wrapper.vm.$nextTick(); + + wrapper.vm.search(event , event.target.value, 'input'); + await wrapper.vm.$nextTick(); + + await wrapper.setProps({ + suggestions: [ + {"name": "Bahrain", "code": "BH"} + ] + }); + + expect(wrapper.find('.p-autocomplete-items').exists()).toBe(true); + expect(wrapper.findAll('.p-autocomplete-item').length).toBe(1); + }); +}); \ No newline at end of file diff --git a/components/autocomplete/AutoComplete.vue b/components/autocomplete/AutoComplete.vue new file mode 100755 index 000000000..1162a6a78 --- /dev/null +++ b/components/autocomplete/AutoComplete.vue @@ -0,0 +1,1030 @@ + + + + + diff --git a/components/autocomplete/package.json b/components/autocomplete/package.json new file mode 100644 index 000000000..0ae8266a1 --- /dev/null +++ b/components/autocomplete/package.json @@ -0,0 +1,9 @@ +{ + "main": "./autocomplete.cjs.js", + "module": "./autocomplete.esm.js", + "unpkg": "./autocomplete.min.js", + "types": "./AutoComplete.d.ts", + "browser": { + "./sfc": "./AutoComplete.vue" + } +} \ No newline at end of file diff --git a/components/avatar/Avatar.d.ts b/components/avatar/Avatar.d.ts new file mode 100644 index 000000000..f900efec4 --- /dev/null +++ b/components/avatar/Avatar.d.ts @@ -0,0 +1,66 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type AvatarSizeType = 'normal' | 'large' | 'xlarge' | undefined; + +type AvatarShapeType = 'square' | 'circle' | undefined; + +export interface AvatarProps { + /** + * Defines the text to display. + */ + label?: string | undefined; + /** + * Defines the icon to display. + */ + icon?: string | undefined; + /** + * Defines the image to display. + */ + image?: string | undefined; + /** + * Size of the element, valid options are 'normal', 'large' and 'xlarge'. + * @see AvatarSizeType + * Default value is 'normal'. + */ + size?: AvatarSizeType; + /** + * Shape of the element, valid options are 'square' and 'circle'. + * @see AvatarShapeType + * Default value is 'square'. + */ + shape?: AvatarShapeType; +} + +export interface AvatarSlots { + /** + * Content can easily be customized with the default slot instead of using the built-in modes. + */ + default: () => VNode[]; +} + +export declare type AvatarEmits = { + /** + * Triggered when an error occurs while loading an image file. + */ + error: () => void; +} + +declare class Avatar extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Avatar: GlobalComponentConstructor + } +} + +/** + * + * Avatar represents people using icons, labels and images. + * + * Demos: + * + * - [Avatar](https://www.primefaces.org/primevue/showcase/#/avatar) + * + */ +export default Avatar; diff --git a/components/avatar/Avatar.spec.js b/components/avatar/Avatar.spec.js new file mode 100644 index 000000000..896df5da5 --- /dev/null +++ b/components/avatar/Avatar.spec.js @@ -0,0 +1,20 @@ +import { mount } from '@vue/test-utils'; +import Avatar from './Avatar.vue'; + +describe('Avatar.vue', () => { + it('should exist', () => { + const wrapper = mount(Avatar, { + props: { + label: 'T', + size: 'large', + shape: 'circle' + } + }); + + expect(wrapper.find('.p-avatar.p-component').exists()).toBe(true); + expect(wrapper.find('.p-avatar-lg').exists()).toBe(true); + expect(wrapper.find('.p-avatar-circle').exists()).toBe(true); + expect(wrapper.find('.p-avatar-text').exists()).toBe(true); + expect(wrapper.find('.p-avatar-text').text()).toBe('T'); + }); +}); \ No newline at end of file diff --git a/components/avatar/Avatar.vue b/components/avatar/Avatar.vue new file mode 100644 index 000000000..6e9b0d2a6 --- /dev/null +++ b/components/avatar/Avatar.vue @@ -0,0 +1,87 @@ + + + + + diff --git a/components/avatar/package.json b/components/avatar/package.json new file mode 100644 index 000000000..eb047f377 --- /dev/null +++ b/components/avatar/package.json @@ -0,0 +1,9 @@ +{ + "main": "./avatar.cjs.js", + "module": "./avatar.esm.js", + "unpkg": "./avatar.min.js", + "types": "./Avatar.d.ts", + "browser": { + "./sfc": "./Avatar.vue" + } +} \ No newline at end of file diff --git a/components/avatargroup/AvatarGroup.d.ts b/components/avatargroup/AvatarGroup.d.ts new file mode 100644 index 000000000..858ebee34 --- /dev/null +++ b/components/avatargroup/AvatarGroup.d.ts @@ -0,0 +1,33 @@ +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface AvatarGroupProps { +} + +export interface AvatarGroupSlots { +} + +export declare type AvatarGroupEmits = { +} + +declare class AvatarGroup extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + AvatarGroup: GlobalComponentConstructor + } +} + +/** + * + * A set of Avatars can be displayed together using the AvatarGroup component. + * + * Helper Components: + * + * - Avatar + * + * Demos: + * + * - [AvatarGroup](https://www.primefaces.org/primevue/showcase/#/avatar) + * + */ +export default AvatarGroup; diff --git a/components/avatargroup/AvatarGroup.spec.js b/components/avatargroup/AvatarGroup.spec.js new file mode 100644 index 000000000..4f0e69de0 --- /dev/null +++ b/components/avatargroup/AvatarGroup.spec.js @@ -0,0 +1,21 @@ +import { mount } from '@vue/test-utils'; +import AvatarGroup from './AvatarGroup.vue'; +import Avatar from '@/components/avatar/Avatar.vue'; + +describe('AvatarGroup.vue', () => { + it('should exist', () => { + const wrapper = mount(AvatarGroup, { + global: { + components: { + Avatar + } + }, + slots: { + default: '' + } + }); + + expect(wrapper.find('.p-avatar-group.p-component').exists()).toBe(true); + expect(wrapper.find('.p-avatar.p-component').exists()).toBe(true); + }); +}); \ No newline at end of file diff --git a/components/avatargroup/AvatarGroup.vue b/components/avatargroup/AvatarGroup.vue new file mode 100644 index 000000000..e0871a443 --- /dev/null +++ b/components/avatargroup/AvatarGroup.vue @@ -0,0 +1,22 @@ + + + + + diff --git a/components/avatargroup/package.json b/components/avatargroup/package.json new file mode 100644 index 000000000..5ade6d698 --- /dev/null +++ b/components/avatargroup/package.json @@ -0,0 +1,9 @@ +{ + "main": "./avatargroup.cjs.js", + "module": "./avatargroup.esm.js", + "unpkg": "./avatargroup.min.js", + "types": "./AvatarGroup.d.ts", + "browser": { + "./sfc": "./AvatarGroup.vue" + } +} \ No newline at end of file diff --git a/components/badge/Badge.css b/components/badge/Badge.css new file mode 100644 index 000000000..fe55f385a --- /dev/null +++ b/components/badge/Badge.css @@ -0,0 +1,32 @@ +.p-badge { + display: inline-block; + border-radius: 10px; + text-align: center; + padding: 0 .5rem; +} + +.p-overlay-badge { + position: relative; +} + +.p-overlay-badge .p-badge { + position: absolute; + top: 0; + right: 0; + transform: translate(50%,-50%); + transform-origin: 100% 0; + margin: 0; +} + +.p-badge-dot { + width: .5rem; + min-width: .5rem; + height: .5rem; + border-radius: 50%; + padding: 0; +} + +.p-badge-no-gutter { + padding: 0; + border-radius: 50%; +} \ No newline at end of file diff --git a/components/badge/Badge.d.ts b/components/badge/Badge.d.ts new file mode 100644 index 000000000..2d55781d5 --- /dev/null +++ b/components/badge/Badge.d.ts @@ -0,0 +1,52 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type BadgeSeverityType = 'info' | 'success' | 'warning' | 'danger' | undefined; + +type BadgeSizeType = 'large' | 'xlarge' | undefined; + +export interface BadgeProps { + /** + * Value to display inside the badge. + */ + value?: any; + /** + * Severity type of the badge. + * @see BadgeSeverityType + */ + severity?: BadgeSeverityType; + /** + * Size of the badge, valid options are 'large' and 'xlarge'. + * @see BadgeSizeType + */ + size?: BadgeSizeType; +} + +export interface BadgeSlots { + /** + * Content can easily be customized with the default slot instead of using the built-in display. + */ + default: () => VNode[]; +} + +export declare type BadgeEmits = { +} + +declare class Badge extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Badge: GlobalComponentConstructor + } +} + +/** + * + * Badge is a small status indicator for another element. + * + * Demos: + * + * - [Badge](https://www.primefaces.org/primevue/showcase/#/badge) + * + */ +export default Badge; diff --git a/components/badge/Badge.spec.js b/components/badge/Badge.spec.js new file mode 100644 index 000000000..1e0e3aa1c --- /dev/null +++ b/components/badge/Badge.spec.js @@ -0,0 +1,33 @@ +import { mount } from '@vue/test-utils'; +import Badge from './Badge.vue'; +import Button from '@/components/button/Button.vue'; + +describe('Badge.vue', () => { + it('should exist', () => { + const wrapper = mount(Badge, { + props: { + value: '29', + severity: 'warning', + size: 'large' + } + }); + + expect(wrapper.find('.p-badge.p-component').exists()).toBe(true); + expect(wrapper.find('.p-badge-warning').exists()).toBe(true); + expect(wrapper.find('.p-badge-lg').exists()).toBe(true); + expect(wrapper.find('.p-badge-no-gutter').exists()).toBe(false); + }); + + it('badge classes should exist', () => { + const wrapper = mount({ + template: ' + + + + +

The story begins as Don Vito Corleone, the head of a New York Mafia family.

+
+
+ `, + components: { + BlockUI, + Panel, + Button + }, + data() { + return { + blockedPanel: false + } + }, + methods: { + blockPanel() { + this.blockedPanel = true; + }, + unblockPanel() { + this.blockedPanel = false; + } + } + }); + + expect(wrapper.find('.p-blockui-container').exists()).toBe(true); + + const buttons = wrapper.findAll('.p-button'); + + await buttons[0].trigger('click'); + + expect(wrapper.find('.p-blockui').exists()).toBe(true); + expect(wrapper.find('.p-blockui').classes()).toContain('p-component-overlay-enter'); + expect(wrapper.find('.p-blockui').attributes().style).toEqual('z-index: 1101;'); + + await buttons[1].trigger('click'); + + expect(wrapper.find('.p-blockui').classes()).toContain('p-component-overlay-leave'); + }); +}); diff --git a/components/blockui/BlockUI.vue b/components/blockui/BlockUI.vue new file mode 100755 index 000000000..7d2a387d1 --- /dev/null +++ b/components/blockui/BlockUI.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/components/blockui/package.json b/components/blockui/package.json new file mode 100644 index 000000000..9909ce560 --- /dev/null +++ b/components/blockui/package.json @@ -0,0 +1,9 @@ +{ + "main": "./blockui.cjs.js", + "module": "./blockui.esm.js", + "unpkg": "./blockui.min.js", + "types": "./BlockUI.d.ts", + "browser": { + "./sfc": "./BlockUI.vue" + } +} \ No newline at end of file diff --git a/components/breadcrumb/Breadcrumb.d.ts b/components/breadcrumb/Breadcrumb.d.ts new file mode 100755 index 000000000..7c526cf44 --- /dev/null +++ b/components/breadcrumb/Breadcrumb.d.ts @@ -0,0 +1,58 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; +import { MenuItem } from '../menuitem'; + +export interface BreadcrumbProps { + /** + * An array of menuitems. + */ + model?: MenuItem[]; + /** + * Configuration for the home icon. + */ + home?: MenuItem; + /** + * Whether to apply 'router-link-active-exact' class if route exactly matches the item path. + * Default value is true. + */ + exact?: boolean; +} + +export interface BreadcrumbSlots { + /** + * Custom item template. + * @param {Object} scope - item slot's params. + */ + item: (scope: { + /** + * Menuitem instance + */ + item: MenuItem; + }) => VNode[]; +} + +export declare type BreadcrumbEmits = { +} + +declare class Breadcrumb extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Breadcrumb: GlobalComponentConstructor + } +} + +/** + * + * Breadcrumb provides contextual information about page hierarchy. + * + * Helper API: + * + * - [MenuItem](https://www.primefaces.org/primevue/showcase/#/menumodel) + * + * Demos: + * + * - [Breadcrumb](https://www.primefaces.org/primevue/showcase/#/breadcrumb) + * + */ +export default Breadcrumb; diff --git a/components/breadcrumb/Breadcrumb.spec.js b/components/breadcrumb/Breadcrumb.spec.js new file mode 100644 index 000000000..60a2edb7e --- /dev/null +++ b/components/breadcrumb/Breadcrumb.spec.js @@ -0,0 +1,26 @@ +import { mount } from '@vue/test-utils'; +import Breadcrumb from './Breadcrumb.vue'; + +describe('Breadcrumb', () => { + it('should exist', () => { + const wrapper = mount(Breadcrumb, { + global: { + stubs: ['router-link'] + }, + props: { + home: {icon: 'pi pi-home', to: '/'}, + model: [ + {label: 'Computer'}, + {label: 'Notebook'}, + {label: 'Accessories'}, + {label: 'Backpacks'}, + {label: 'Item'} + ] + } + }); + + expect(wrapper.find('.p-breadcrumb.p-component').exists()).toBe(true); + expect(wrapper.findAll('.p-breadcrumb-chevron').length).toBe(5); + expect(wrapper.findAll('.p-menuitem-text').length).toBe(5); + }); +}); \ No newline at end of file diff --git a/components/breadcrumb/Breadcrumb.vue b/components/breadcrumb/Breadcrumb.vue new file mode 100755 index 000000000..b979a4843 --- /dev/null +++ b/components/breadcrumb/Breadcrumb.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/components/breadcrumb/BreadcrumbItem.vue b/components/breadcrumb/BreadcrumbItem.vue new file mode 100755 index 000000000..2083a30a9 --- /dev/null +++ b/components/breadcrumb/BreadcrumbItem.vue @@ -0,0 +1,65 @@ + + + diff --git a/components/breadcrumb/package.json b/components/breadcrumb/package.json new file mode 100644 index 000000000..b84efc8a1 --- /dev/null +++ b/components/breadcrumb/package.json @@ -0,0 +1,10 @@ +{ + "main": "./breadcrumb.cjs.js", + "module": "./breadcrumb.esm.js", + "unpkg": "./breadcrumb.min.js", + "types": "./Breadcrumb.d.ts" + , + "browser": { + "./sfc": "./Breadcrumb.vue" + } +} \ No newline at end of file diff --git a/components/button/Button.css b/components/button/Button.css new file mode 100755 index 000000000..f937b65f9 --- /dev/null +++ b/components/button/Button.css @@ -0,0 +1,68 @@ +.p-button { + margin: 0; + display: inline-flex; + cursor: pointer; + user-select: none; + align-items: center; + vertical-align: bottom; + text-align: center; + overflow: hidden; + position: relative; +} + +.p-button-label { + flex: 1 1 auto; +} + +.p-button-icon-right { + order: 1; +} + +.p-button:disabled { + cursor: default; +} + +.p-button-icon-only { + justify-content: center; +} + +.p-button-icon-only .p-button-label { + visibility: hidden; + width: 0; + flex: 0 0 auto; +} + +.p-button-vertical { + flex-direction: column; +} + +.p-button-icon-bottom { + order: 2; +} + +.p-buttonset .p-button { + margin: 0; +} + +.p-buttonset .p-button:not(:last-child) { + border-right: 0 none; +} + +.p-buttonset .p-button:not(:first-of-type):not(:last-of-type) { + border-radius: 0; +} + +.p-buttonset .p-button:first-of-type { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.p-buttonset .p-button:last-of-type { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.p-buttonset .p-button:focus { + position: relative; + z-index: 1; +} \ No newline at end of file diff --git a/components/button/Button.d.ts b/components/button/Button.d.ts new file mode 100755 index 000000000..992b4c166 --- /dev/null +++ b/components/button/Button.d.ts @@ -0,0 +1,74 @@ +import { ButtonHTMLAttributes, VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type ButtonIconPosType = 'left' | 'right' | 'top' | 'bottom' | undefined; + +export interface ButtonProps extends ButtonHTMLAttributes { + /** + * Inline style of the button. + */ + style?: any; + /** + * Style class of the button. + */ + class?: any; + /** + * Text of the button. + */ + label?: string | undefined; + /** + * Name of the icon. + */ + icon?: string | undefined; + /** + * Position of the icon, valid values are 'left', 'right', 'bottom' and 'top'. + * Default value is 'left'. + */ + iconPos?: ButtonIconPosType; + /** + * Value of the badge. + */ + badge?: string | undefined; + /** + * Style class of the badge. + */ + badgeClass?: string | undefined; + /** + * Whether the button is in loading state. + */ + loading?: boolean | undefined; + /** + * Icon to display in loading state. + * Default value is 'pi pi-spinner pi-spin'. + */ + loadingIcon?: string | undefined; +} + +export interface ButtonSlots { + /** + * Custom content such as icons, images and text can be placed inside the button via the default slot. Note that when slot is used, label, icon and badge properties are not included. + */ + default: () => VNode[]; +} + +export declare type ButtonEmits = { +} + +declare class Button extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Button: GlobalComponentConstructor'); + }) +}); \ No newline at end of file diff --git a/components/button/Button.vue b/components/button/Button.vue new file mode 100755 index 000000000..cfa39fe8a --- /dev/null +++ b/components/button/Button.vue @@ -0,0 +1,84 @@ + + + diff --git a/components/button/package.json b/components/button/package.json new file mode 100644 index 000000000..30406f96f --- /dev/null +++ b/components/button/package.json @@ -0,0 +1,9 @@ +{ + "main": "./button.cjs.js", + "module": "./button.esm.js", + "unpkg": "./button.min.js", + "types": "./Button.d.ts", + "browser": { + "./sfc": "./Button.vue" + } +} \ No newline at end of file diff --git a/components/calendar/Calendar.d.ts b/components/calendar/Calendar.d.ts new file mode 100755 index 000000000..28b812226 --- /dev/null +++ b/components/calendar/Calendar.d.ts @@ -0,0 +1,400 @@ +import { HTMLAttributes, InputHTMLAttributes, VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type CalendarValueType = Date | Date[] | undefined; + +type CalendarSlotDateType = { day: number; month: number; year: number; today: boolean; selectable: boolean } + +type CalendarSelectionModeType = 'single' | 'multiple' | 'range' | undefined; + +type CalendarViewType = 'date' | 'month' | 'year' | undefined; + +type CalendarHourFormatType = '12' | '24' | undefined; + +type CalendarAppendToType = 'body' | 'self' | string | undefined | HTMLElement; + +export interface CalendarResponsiveOptions { + /** + * Breakpoint for responsive mode. Exp; @media screen and (max-width: ${breakpoint}) {...} + */ + breakpoint: string; + /** + * The number of visible months on breakpoint. + */ + numMonths: number; +} + +export interface CalendarMonthChangeEvent { + /** + * New month. + */ + month: number; + /** + * New year. + */ + year: number; +} + +export interface CalendarYearChangeEvent { + /** + * New month. + */ + month: number; + /** + * New year. + */ + year: number; +} + +export interface CalendarBlurEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Input value + */ + value: string; +} + +export interface CalendarProps { + /** + * Value of the component. + */ + modelValue?: CalendarValueType; + /** + * Defines the quantity of the selection, valid values are 'single', 'multiple' and 'range'. + * @see CalendarSelectionModeType + * Default value is 'single'. + */ + selectionMode?: CalendarSelectionModeType; + /** + * Format of the date. Defaults to PrimeVue Locale configuration. + */ + dateFormat?: string | undefined; + /** + * When enabled, displays the calendar as inline instead of an overlay. + */ + inline?: boolean | undefined; + /** + * Whether to display dates in other months (non-selectable) at the start or end of the current month. To make these days selectable use the selectOtherMonths option. + * Default value is true. + */ + showOtherMonths?: boolean | undefined; + /** + * Whether days in other months shown before or after the current month are selectable. This only applies if the showOtherMonths option is set to true. + */ + selectOtherMonths?: boolean | undefined; + /** + * When enabled, displays a button with icon next to input. + */ + showIcon?: boolean | undefined; + /** + * Icon of the calendar button. + * Default value is 'pi pi-calendar'. + */ + icon?: string | undefined; + /** + * Number of months to display. + * Default value is 1. + */ + numberOfMonths?: number | undefined; + /** + * An array of options for responsive design. + * @see CalendarResponsiveOptions + */ + responsiveOptions?: CalendarResponsiveOptions[]; + /** + * Type of view to display, valid values are 'date', 'month' and 'year'. + * @see CalendarViewType + * Default value is 'date'. + */ + view?: CalendarViewType; + /** + * When enabled, calendar overlay is displayed as optimized for touch devices. + */ + touchUI?: boolean | undefined; + /** + * Whether the month should be rendered as a dropdown instead of text. + * + * @deprecated since version 3.9.0, Navigator is always on. + */ + monthNavigator?: boolean | undefined; + /** + * Whether the year should be rendered as a dropdown instead of text. + * + * @deprecated since version 3.9.0, Navigator is always on. + */ + yearNavigator?: boolean | undefined; + /** + * The range of years displayed in the year drop-down in (nnnn:nnnn) format such as (2000:2020). + * + * @deprecated since version 3.9.0, Years are based on decades by default. + */ + yearRange?: string | undefined; + /** + * The minimum selectable date. + */ + minDate?: Date | undefined; + /** + * The maximum selectable date. + */ + maxDate?: Date | undefined; + /** + * Array with dates to disable. + */ + disabledDates?: Date[] | undefined; + /** + * Array with disabled weekday numbers. + */ + disabledDays?: number[] | undefined; + /** + * Maximum number of selectable dates in multiple mode. + */ + maxDateCount?: number | undefined; + /** + * When disabled, datepicker will not be visible with input focus. + * Default value is true. + */ + showOnFocus?: boolean | undefined; + /** + * Whether to automatically manage layering. + * Default value is true. + */ + autoZIndex?: boolean | undefined; + /** + * Base zIndex value to use in layering. + * Default value is 0. + */ + baseZIndex?: number | undefined; + /** + * Whether to display today and clear buttons at the footer. + */ + showButtonBar?: boolean | undefined; + /** + * The cutoff year for determining the century for a date. + * Default value is +10. + */ + shortYearCutoff?: string | undefined; + /** + * Whether to display timepicker. + */ + showTime?: boolean | undefined; + /** + * Whether to display timepicker only. + */ + timeOnly?: boolean | undefined; + /** + * Specifies 12 or 24 hour format. + */ + hourFormat?: CalendarHourFormatType; + /** + * Hours to change per step. + * Default value is 1. + */ + stepHour?: number | undefined; + /** + * Minutes to change per step. + * Default value is 1. + */ + stepMinute?: number | undefined; + /** + * Seconds to change per step. + * Default value is 1. + */ + stepSecond?: number | undefined; + /** + * Whether to show the seconds in time picker. + */ + showSeconds?: boolean | undefined; + /** + * Whether to hide the overlay on date selection when showTime is enabled. + */ + hideOnDateTimeSelect?: boolean | undefined; + /** + * Whether to hide the overlay on date selection is completed when selectionMode is range. + */ + hideOnRangeSelection?: boolean | undefined; + /** + * Separator of time selector. + * Default value is ':'. + */ + timeSeparator?: string | undefined; + /** + * When enabled, calendar will show week numbers. + */ + showWeek?: boolean | undefined; + /** + * Wheter to allow prevents entering the date manually via typing. + * Default value is true. + */ + manualInput?: boolean | undefined; + /** + * When present, it specifies that the component should be disabled. + */ + disabled?: boolean | undefined; + /** + * When present, it specifies that an input field is read-only. + */ + readonly?: boolean | undefined; + /** + * Placeholder text for the input. + */ + placeholder?: string | undefined; + /** + * A valid query selector or an HTMLElement to specify where the overlay gets attached. Special keywords are 'body' for document body and 'self' for the element itself. + * @see CalendarAppendToType + * Default value is 'body'. + */ + appendTo?: CalendarAppendToType; + /** + * Identifier of the element. + */ + id?: string | undefined; + /** + * Identifier of the underlying input element. + */ + inputId?: string | undefined; + /** + * Inline style of the input field. + */ + inputStyle?: any | undefined; + /** + * Style class of the input field. + */ + inputClass?: any | undefined; + /** + * Uses to pass all properties of the HTMLInputElement to the focusable input element inside the component. + */ + inputProps?: InputHTMLAttributes | undefined; + /** + * Inline style of the overlay panel. + */ + panelStyle?: any | undefined; + /** + * Style class of the overlay panel. + */ + panelClass?: any | undefined; + /** + * Uses to pass all properties of the HTMLDivElement to the overlay panel inside the component. + */ + panelProps?: HTMLAttributes | undefined; + /** + * Establishes relationships between the component and label(s) where its value should be one or more element IDs. + */ + 'aria-labelledby'?: string | undefined; + /** + * Establishes a string value that labels the component. + */ + 'aria-label'?: string | undefined; +} + +export interface CalendarSlots { + /** + * Custom header template of panel. + */ + header: () => VNode[]; + /** + * Custom footer template of panel. + */ + footer: () => VNode[]; + /** + * Custom date template. + * @param {Object} scope - date slot's params. + */ + date: (scope: { + /** + * Value of the component. + */ + date: CalendarSlotDateType; + }) => VNode[]; + /** + * Custom decade template. + * @param {CalendarDecadeSlot} scope - decade slot's params. + */ + decade: (scope: { + /** + * An array containing the start and and year of a decade to display at header of the year picker. + */ + years: string[] | undefined; + }) => VNode[]; +} + +export declare type CalendarEmits = { + /** + * Emitted when the value changes. + * @param {CalendarValueType} value - New value. + */ + 'update:modelValue': (value: CalendarValueType) => void; + /** + * Callback to invoke when input field is being typed. + * @param {Event} event - Browser event + */ + 'input': (event: Event) => void; + /** + * Callback to invoke when a date is selected. + * @param {Date} value - Selected value. + */ + 'date-select': (value: Date) => void; + /** + * Callback to invoke when datepicker panel is shown. + */ + 'show': () => void; + /** + * Callback to invoke when datepicker panel is hidden. + */ + 'hide': () => void; + /** + * Callback to invoke when today button is clicked. + * @param {Date} date - Today as a date instance. + */ + 'today-click': (date: Date) => void; + /** + * Callback to invoke when clear button is clicked. + * @param {Event} event - Browser event. + */ + 'clear-click': (event: Event) => void; + /** + * Callback to invoke when a month is changed using the navigators. + * @param {CalendarMonthChangeEvent} event - Custom month change event. + */ + 'month-change': (event: CalendarMonthChangeEvent) => void; + /** + * Callback to invoke when a year is changed using the navigators. + * @param {CalendarYearChangeEvent} event - Custom year change event. + */ + 'year-change': (event: CalendarYearChangeEvent) => void; + /** + * Callback to invoke on focus of input field. + * @param {Event} event - Focus event + */ + 'focus': (event: Event) => void; + /** + * Callback to invoke on blur of input field. + * @param {CalendarBlurEvent} event - Blur event + */ + 'blur': (event: CalendarBlurEvent) => void; + /** + * Callback to invoke when a key is pressed. + */ + 'keydown': (event: Event) => void; +} + +declare class Calendar extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Calendar: GlobalComponentConstructor + } +} + +/** + * + * Calendar is an input component to select a date. + * + * Demos: + * + * - [Calendar](https://www.primefaces.org/primevue/showcase/#/calendar) + * + */ +export default Calendar; diff --git a/components/calendar/Calendar.spec.js b/components/calendar/Calendar.spec.js new file mode 100644 index 000000000..85470bb6d --- /dev/null +++ b/components/calendar/Calendar.spec.js @@ -0,0 +1,53 @@ +import { mount } from '@vue/test-utils'; +import PrimeVue from '@/components/config/PrimeVue'; +import Calendar from './Calendar.vue'; + +describe('Calendar.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Calendar, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true + } + }, + props: { + modelValue: new Date() + } + }); + }); + it('should exist', async() => { + expect(wrapper.find('.p-calendar.p-component').exists()).toBe(true); + expect(wrapper.find('.p-inputtext').exists()).toBe(true); + + let input = wrapper.find('.p-inputtext'); + + await input.trigger('focus'); + + expect(wrapper.find('.p-datepicker.p-component').exists()).toBe(true); + expect(wrapper.find('.p-datepicker-today').exists()).toBe(true); + expect(wrapper.find('.p-highlight').exists()).toBe(true); + expect(wrapper.find('.p-highlight').text()).toEqual(new Date().getDate().toString()); + }); + + it('should select a date', async () => { + await wrapper.setProps({ inline: true }); + + const event = {day: 8, month: 2, year: 2022, today: false, selectable: true}; + + const onDateSelect = jest.spyOn(wrapper.vm, 'onDateSelect'); + + await wrapper.vm.onDateSelect({currentTarget: {focus: () => {}}}, event); + expect(onDateSelect).toHaveBeenCalled() + }); + it('should calculate the correct view date when in range mode', async () => { + const dateOne = new Date(); + const dateTwo = new Date(); + dateTwo.setFullYear(dateOne.getFullYear(), dateOne.getMonth(), dateOne.getDate() + 1) + await wrapper.setProps({ selectionMode: 'range', showTime: true, modelValue: [dateOne, dateTwo] }); + + expect(wrapper.vm.viewDate).toEqual(dateTwo) + }); +}); diff --git a/components/calendar/Calendar.vue b/components/calendar/Calendar.vue new file mode 100755 index 000000000..36ddf47c8 --- /dev/null +++ b/components/calendar/Calendar.vue @@ -0,0 +1,2815 @@ + + + + + diff --git a/components/calendar/package.json b/components/calendar/package.json new file mode 100644 index 000000000..d64211699 --- /dev/null +++ b/components/calendar/package.json @@ -0,0 +1,9 @@ +{ + "main": "./calendar.cjs.js", + "module": "./calendar.esm.js", + "unpkg": "./calendar.min.js", + "types": "./Calendar.d.ts", + "browser": { + "./sfc": "./Calendar.vue" + } +} \ No newline at end of file diff --git a/components/card/Card.d.ts b/components/card/Card.d.ts new file mode 100755 index 000000000..eaa6f573a --- /dev/null +++ b/components/card/Card.d.ts @@ -0,0 +1,50 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface CardProps { +} + +export interface CardSlots { + /** + * Custom header template. + */ + header: () => VNode[]; + /** + * Custom title template. + */ + title: () => VNode[]; + /** + * Custom subtitle template. + */ + subtitle: () => VNode[]; + /** + * Custom content template. + */ + content: () => VNode[]; + /** + * Custom footer template. + */ + footer: () => VNode[]; +} + +export declare type CardEmits = { +} + +declare class Card extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Card: GlobalComponentConstructor + } +} + +/** + * + * Card is a flexible container component. + * + * Demos: + * + * - [Card](https://www.primefaces.org/primevue/showcase/#/card) + * + */ +export default Card; diff --git a/components/card/Card.spec.js b/components/card/Card.spec.js new file mode 100644 index 000000000..65870dace --- /dev/null +++ b/components/card/Card.spec.js @@ -0,0 +1,23 @@ +import { mount } from '@vue/test-utils'; +import Card from './Card.vue'; + +describe('Card.vue', () => { + it('should exist', () => { + const wrapper = mount(Card, { + slots: { + header: 'Advanced Card Header', + title: 'Advanced Card', + subtitle: 'Advanced subtitle', + content: '

Lorem ipsum dolor sit amet, consectetur adipisicing elit.

', + footer: 'Advanced Card Footer' + } + }); + + expect(wrapper.find('.p-card.p-component').exists()).toBe(true); + expect(wrapper.find('.p-card-header').text()).toBe('Advanced Card Header'); + expect(wrapper.find('.p-card-title').text()).toBe('Advanced Card'); + expect(wrapper.find('.p-card-subtitle').text()).toBe('Advanced subtitle'); + expect(wrapper.find('.p-card-content').text()).toBe('Lorem ipsum dolor sit amet, consectetur adipisicing elit.'); + expect(wrapper.find('.p-card-footer').text()).toBe('Advanced Card Footer'); + }); +}); \ No newline at end of file diff --git a/components/card/Card.vue b/components/card/Card.vue new file mode 100755 index 000000000..4fdc3e1c9 --- /dev/null +++ b/components/card/Card.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/components/card/package.json b/components/card/package.json new file mode 100644 index 000000000..7158047e2 --- /dev/null +++ b/components/card/package.json @@ -0,0 +1,9 @@ +{ + "main": "./card.cjs.js", + "module": "./card.esm.js", + "unpkg": "./card.min.js", + "types": "./Card.d.ts", + "browser": { + "./sfc": "./Card.vue" + } +} \ No newline at end of file diff --git a/components/carousel/Carousel.d.ts b/components/carousel/Carousel.d.ts new file mode 100755 index 000000000..a0803e60f --- /dev/null +++ b/components/carousel/Carousel.d.ts @@ -0,0 +1,130 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type CarouselOrientationType = 'horizontal' | 'vertical' | undefined; + +export interface CarouselResponsiveOptions { + /** + * Breakpoint for responsive mode. Exp; @media screen and (max-width: ${breakpoint}) {...} + */ + breakpoint: string; + /** + * The number of visible items on breakpoint. + */ + numVisible: number; + /** + * The number of scrolled items on breakpoint. + */ + numScroll: number; +} + +export interface CarouselProps { + /** + * An array of objects to display. + */ + value?: any | undefined; + /** + * Index of the first item. + * Default value is 0. + */ + page?: number | undefined; + /** + * Number of items per page. + * Default value is 1. + */ + numVisible?: number | undefined; + /** + * Number of items to scroll. + * Default value is 1. + */ + numScroll?: number | undefined; + /** + * An array of options for responsive design. + * @see CarouselResponsiveOptions + */ + responsiveOptions?: CarouselResponsiveOptions[] | undefined; + /** + * Specifies the layout of the component, valid values are 'horizontal' and 'vertical'. + * @see CarouselOrientationType + * Default value is 'horizontal'. + */ + orientation?: CarouselOrientationType; + /** + * Height of the viewport in vertical layout. + * Default value is '300px'. + */ + verticalViewPortHeight?: string | undefined; + /** + * Style class of the viewport container. + */ + containerClass?: any; + /** + * Style class of main content. + */ + contentClass?: any; + /** + * Style class of the indicator items. + */ + indicatorsContentClass?: any; + /** + * Defines if scrolling would be infinite. + */ + circular?: boolean | undefined; + /** + * Time in milliseconds to scroll items automatically. + * Default value is 0. + */ + autoplayInterval?: number | undefined; +} + +export interface CarouselSlots { + /** + * Custom content for each item. + * @param {Object} scope - item slot's params. + */ + item: (scope: { + /** + * Data of the component + */ + data: any; + /** + * Index of the item + */ + index: number; + }) => VNode[]; + /** + * Custom header template. + */ + header: () => VNode[]; + /** + * Custom footer template. + */ + footer: () => VNode[]; +} + +export declare type CarouselEmits = { + /** + * Emitted when the page changes. + * @param {number} value - New page value. + */ + 'update:page': (value: number) => void; +} + +declare class Carousel extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Carousel: GlobalComponentConstructor + } +} + +/** + * + * Carousel is a content slider featuring various customization options. + * + * Demos: + * + * - [Carousel](https://www.primefaces.org/primevue/showcase/#/carousel) + * + */ +export default Carousel; diff --git a/components/carousel/Carousel.spec.js b/components/carousel/Carousel.spec.js new file mode 100644 index 000000000..c324e2f72 --- /dev/null +++ b/components/carousel/Carousel.spec.js @@ -0,0 +1,84 @@ +import { mount } from '@vue/test-utils'; +import Carousel from './Carousel.vue'; + +describe('Carousel.vue', () => { + it('should exist', async () => { + const wrapper = mount(Carousel, { + props: { + value: [ + { + "id": "1000", + "code": "vbb124btr", + "name": "Game Controller", + "description": "Product Description", + "image": "game-controller.jpg", + "price": 99, + "category": "Electronics", + "quantity": 2, + "inventoryStatus": "LOWSTOCK", + "rating": 4 + }, + { + "id": "1001", + "code": "nvklal433", + "name": "Black Watch", + "description": "Product Description", + "image": "black-watch.jpg", + "price": 72, + "category": "Accessories", + "quantity": 61, + "inventoryStatus": "INSTOCK", + "rating": 4 + }, + { + "id": "1002", + "code": "zz21cz3c1", + "name": "Blue Band", + "description": "Product Description", + "image": "blue-band.jpg", + "price": 79, + "category": "Fitness", + "quantity": 2, + "inventoryStatus": "LOWSTOCK", + "rating": 3 + }, + { + "id": "1003", + "code": "244wgerg2", + "name": "Blue T-Shirt", + "description": "Product Description", + "image": "blue-t-shirt.jpg", + "price": 29, + "category": "Clothing", + "quantity": 25, + "inventoryStatus": "INSTOCK", + "rating": 5 + } + ] + }, + slots: { + header: 'Basic', + item: ` + ` + } + }); + + expect(wrapper.findAll('.p-carousel-item').length).toBe(4); + + const firstItem = wrapper.findAll('.p-carousel-item')[0]; + expect(firstItem.classes()).toContain('p-carousel-item-active'); + + const nextBtn = wrapper.find('.p-carousel-next'); + await nextBtn.trigger('click'); + + expect(firstItem.classes()).not.toContain('p-carousel-item-active'); + }) +}); \ No newline at end of file diff --git a/components/carousel/Carousel.vue b/components/carousel/Carousel.vue new file mode 100755 index 000000000..2daf420a1 --- /dev/null +++ b/components/carousel/Carousel.vue @@ -0,0 +1,599 @@ + + + + + diff --git a/components/carousel/package.json b/components/carousel/package.json new file mode 100644 index 000000000..14eb00e41 --- /dev/null +++ b/components/carousel/package.json @@ -0,0 +1,9 @@ +{ + "main": "./carousel.cjs.js", + "module": "./carousel.esm.js", + "unpkg": "./carousel.min.js", + "types": "./Carousel.d.ts", + "browser": { + "./sfc": "./Carousel.vue" + } +} \ No newline at end of file diff --git a/components/cascadeselect/CascadeSelect.d.ts b/components/cascadeselect/CascadeSelect.d.ts new file mode 100644 index 000000000..0219e76ca --- /dev/null +++ b/components/cascadeselect/CascadeSelect.d.ts @@ -0,0 +1,264 @@ +import { HTMLAttributes, InputHTMLAttributes, VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type CascadeSelectOptionLabelType = string | ((data: any) => string) | undefined; + +type CascadeSelectOptionValueType = string | ((data: any) => any) | undefined; + +type CascadeSelectOptionDisabledType = string | ((data: any) => boolean) | undefined; + +type CascadeSelectOptionChildrenType = string[] | string | ((data: any) => any[]) | undefined; + +type CascadeSelectAppendToType = 'body' | 'self' | string | undefined | HTMLElement; + +export interface CascadeSelectChangeEvent { + /** + * Original event. + */ + originalEvent: Event; + /** + * Selected option value. + */ + value: any; +} + +/** + * @extends CascadeSelectChangeEvent + */ +export interface CascadeSelectGroupChangeEvent extends CascadeSelectChangeEvent { } + +export interface CascadeSelectProps { + /** + * Value of the component. + */ + modelValue?: any | undefined; + /** + * An array of selectitems to display as the available options. + */ + options?: any[] | undefined; + /** + * Property name or getter function to use as the label of an option. + * @see CascadeSelectOptionLabelType + */ + optionLabel?: CascadeSelectOptionLabelType; + /** + * Property name or getter function to use as the value of an option, defaults to the option itself when not defined. + * @see CascadeSelectOptionValueType + */ + optionValue?: CascadeSelectOptionValueType; + /** + * Property name or getter function to use as the disabled flag of an option, defaults to false when not defined. + * @see CascadeSelectOptionDisabledType + */ + optionDisabled?: CascadeSelectOptionDisabledType; + /** + * Property name or getter function to use as the label of an option group. + * @see CascadeSelectOptionLabelType + */ + optionGroupLabel?: CascadeSelectOptionLabelType; + /** + * Property name or getter function to retrieve the items of a group. + * @see CascadeSelectOptionChildrenType + */ + optionGroupChildren?: CascadeSelectOptionChildrenType; + /** + * Default text to display when no option is selected. + */ + placeholder?: string | undefined; + /** + * When present, it specifies that the component should be disabled. + */ + disabled?: boolean | undefined; + /** + * A property to uniquely identify an option. + */ + dataKey?: string | undefined; + /** + * Identifier of the underlying input element. + */ + inputId?: string | undefined; + /** + * Inline style of the input field. + */ + inputStyle?: any; + /** + * Style class of the input field. + */ + inputClass?: any; + /** + * Uses to pass all properties of the HTMLInputElement to the focusable input element inside the component. + */ + inputProps?: InputHTMLAttributes | undefined; + /** + * Inline style of the overlay panel. + */ + panelStyle?: any; + /** + * Style class of the overlay panel. + */ + panelClass?: any; + /** + * Uses to pass all properties of the HTMLDivElement to the overlay panel inside the component. + */ + panelProps?: HTMLAttributes | undefined; + /** + * A valid query selector or an HTMLElement to specify where the overlay gets attached. Special keywords are 'body' for document body and 'self' for the element itself. + * @see CascadeSelectAppendToType + * Default value is 'body'. + */ + appendTo?: CascadeSelectAppendToType; + /** + * Whether the dropdown is in loading state. + */ + loading?: boolean | undefined; + /** + * Icon to display in loading state. + * Default value is 'pi pi-spinner pi-spin'. + */ + loadingIcon?: string | undefined; + /** + * Whether to focus on the first visible or selected element when the overlay panel is shown. + * Default value is true. + */ + autoOptionFocus?: boolean | undefined; + /** + * Locale to use in searching. The default locale is the host environment's current locale. + */ + searchLocale?: string | undefined; + /** + * Text to be displayed in hidden accessible field when filtering returns any results. Defaults to value from PrimeVue locale configuration. + * Default value is '{0} results are available'. + */ + searchMessage?: string | undefined; + /** + * Text to be displayed in hidden accessible field when options are selected. Defaults to value from PrimeVue locale configuration. + * Default value is '{0} items selected'. + */ + selectionMessage?: string | undefined; + /** + * Text to be displayed in hidden accessible field when any option is not selected. Defaults to value from PrimeVue locale configuration. + * Default value is 'No selected item'. + */ + emptySelectionMessage?: string | undefined; + /** + * Text to display when filtering does not return any results. Defaults to value from PrimeVue locale configuration. + * Default value is 'No results found'. + */ + emptySearchMessage?: string | undefined; + /** + * Text to be displayed when there are no options available. Defaults to value from PrimeVue locale configuration. + * Default value is 'No available options'. + */ + emptyMessage?: string | undefined; + /** + * Index of the element in tabbing order. + */ + tabindex?: number | string | undefined; + /** + * Establishes relationships between the component and label(s) where its value should be one or more element IDs. + */ + 'aria-labelledby'?: string | undefined; + /** + * Establishes a string value that labels the component. + */ + 'aria-label'?: string | undefined; +} + +export interface CascadeSelectSlots { + /** + * Custom content for each option. + * @param {Object} scope - option slot's params. + */ + option: (scope: { + /** + * Option instance + */ + option: any; + }) => VNode[]; + /** + * Custom value template. + * @param {CascadeSelectValueSlot} scope - value slot's params. + */ + value: (scope: { + /** + * Value of the component + */ + value: any; + /** + * Placeholder text to show + */ + placeholder: string; + }) => VNode[]; + /** + * Custom indicator template. + */ + indicator: () => VNode[]; +} + +export declare type CascadeSelectEmits = { + /** + * Emitted when the value changes. + * @param {*} value - New value. + */ + 'update:modelValue': (value: any) => void; + /** + * Callback to invoke on value change. + * @param { CascadeSelectChangeEvent } event - Custom change event. + */ + 'change': (event: CascadeSelectChangeEvent) => void; + /** + * Callback to invoke when the component receives focus. + * @param {Event} event - Browser event. + */ + 'focus': (event: Event) => void; + /** + * Callback to invoke when the component loses focus. + * @param {Event} event - Browser event. + */ + 'blur': (event: Event) => void; + /** + * Callback to invoke on click. + * @param { Event } event - Browser event. + */ + 'click': (event: Event) => void; + /** + * Callback to invoke when a group changes. + * @param { CascadeSelectGroupChangeEvent } event - Custom change event. + */ + 'group-change': (event: CascadeSelectGroupChangeEvent) => void; + /** + * Callback to invoke before the overlay is shown. + */ + 'before-show': () => void; + /** + * Callback to invoke before the overlay is hidden. + */ + 'before-hide': () => void; + /** + * Callback to invoke when the overlay is shown. + */ + 'show': () => void; + /** + * Callback to invoke when the overlay is hidden. + */ + 'hide': () => void; +} + +declare class CascadeSelect extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + CascadeSelect: GlobalComponentConstructor + } +} + +/** + * + * CascadeSelect displays a nested structure of options. + * + * Demos: + * + * - [CascadeSelect](https://www.primefaces.org/primevue/showcase/#/cascadeselect) + * + */ +export default CascadeSelect; diff --git a/components/cascadeselect/CascadeSelect.spec.js b/components/cascadeselect/CascadeSelect.spec.js new file mode 100644 index 000000000..8c01a9915 --- /dev/null +++ b/components/cascadeselect/CascadeSelect.spec.js @@ -0,0 +1,124 @@ +import { mount } from '@vue/test-utils'; +import PrimeVue from '@/components/config/PrimeVue'; +import CascadeSelect from './CascadeSelect.vue'; + +describe('CascadeSelect.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(CascadeSelect, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true + } + }, + props: { + modelValue: null, + options: [ + { + name: 'Australia', + code: 'AU', + states: [ + { + name: 'New South Wales', + cities: [ + {cname: 'Sydney', code: 'A-SY'}, + {cname: 'Newcastle', code: 'A-NE'}, + {cname: 'Wollongong', code: 'A-WO'} + ] + }, + { + name: 'Queensland', + cities: [ + {cname: 'Brisbane', code: 'A-BR'}, + {cname: 'Townsville', code: 'A-TO'} + ] + }, + + ] + }, + { + name: 'Canada', + code: 'CA', + states: [ + { + name: 'Quebec', + cities: [ + {cname: 'Montreal', code: 'C-MO'}, + {cname: 'Quebec City', code: 'C-QU'} + ] + }, + { + name: 'Ontario', + cities: [ + {cname: 'Ottawa', code: 'C-OT'}, + {cname: 'Toronto', code: 'C-TO'} + ] + }, + + ] + }, + { + name: 'United States', + code: 'US', + states: [ + { + name: 'California', + cities: [ + {cname: 'Los Angeles', code: 'US-LA'}, + {cname: 'San Diego', code: 'US-SD'}, + {cname: 'San Francisco', code: 'US-SF'} + ] + }, + { + name: 'Florida', + cities: [ + {cname: 'Jacksonville', code: 'US-JA'}, + {cname: 'Miami', code: 'US-MI'}, + {cname: 'Tampa', code: 'US-TA'}, + {cname: 'Orlando', code: 'US-OR'} + ] + }, + { + name: 'Texas', + cities: [ + {cname: 'Austin', code: 'US-AU'}, + {cname: 'Dallas', code: 'US-DA'}, + {cname: 'Houston', code: 'US-HO'} + ] + } + ] + } + ], + optionLabel: 'cname', + optionGroupLabel: 'name', + optionGroupChildren: ['states', 'cities'] + } + }); + }); + + it('should exist', async() => { + expect(wrapper.find('.p-cascadeselect.p-component').exists()).toBe(true); + }); + + it('should show list and sublist', async() => { + expect(wrapper.find('.p-cascadeselect.p-component').exists()).toBe(true); + + await wrapper.trigger('click'); + + expect(wrapper.find('.p-cascadeselect-panel.p-cascadeselect-items').exists()).toBe(true); + expect(wrapper.findAll('.p-cascadeselect-item').length).toBe(3); + expect(wrapper.findAll('.p-cascadeselect-item-text')[0].text()).toBe('Australia'); + + const firstGroup = wrapper.findAll('.p-cascadeselect-item-content')[0]; + await firstGroup.trigger('click'); + + expect(wrapper.find('.p-cascadeselect-panel.p-cascadeselect-sublist').exists()).toBe(true); + + const sublist = wrapper.find('.p-cascadeselect-panel.p-cascadeselect-sublist'); + + expect(sublist.findAll('.p-cascadeselect-item.p-cascadeselect-item-group').length).toBe(2); + expect(sublist.findAll('.p-cascadeselect-item-text')[0].text()).toBe('New South Wales'); + }); +}); \ No newline at end of file diff --git a/components/cascadeselect/CascadeSelect.vue b/components/cascadeselect/CascadeSelect.vue new file mode 100644 index 000000000..3f0d021bd --- /dev/null +++ b/components/cascadeselect/CascadeSelect.vue @@ -0,0 +1,851 @@ + + + + + diff --git a/components/cascadeselect/CascadeSelectSub.vue b/components/cascadeselect/CascadeSelectSub.vue new file mode 100644 index 000000000..72bd9413e --- /dev/null +++ b/components/cascadeselect/CascadeSelectSub.vue @@ -0,0 +1,101 @@ + + + diff --git a/components/cascadeselect/package.json b/components/cascadeselect/package.json new file mode 100644 index 000000000..017c6f39e --- /dev/null +++ b/components/cascadeselect/package.json @@ -0,0 +1,9 @@ +{ + "main": "./cascadeselect.cjs.js", + "module": "./cascadeselect.esm.js", + "unpkg": "./cascadeselect.min.js", + "types": "./CascadeSelect.d.ts", + "browser": { + "./sfc": "./CascadeSelect.vue" + } +} \ No newline at end of file diff --git a/components/chart/Chart.d.ts b/components/chart/Chart.d.ts new file mode 100755 index 000000000..eb0ea4ba5 --- /dev/null +++ b/components/chart/Chart.d.ts @@ -0,0 +1,109 @@ +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface ChartSelectEvent { + /** + * Browser event. + */ + originalEvent: Event; + /** + * Selected element. + */ + element: HTMLElement | any; + /** + * Selected dataset. + */ + dataset: any; +} + +export interface ChartProps { + /** + * Type of the chart. + */ + type?: string | undefined; + /** + * Data to display. + */ + data?: object | undefined; + /** + * Options to customize the chart. + */ + options?: object | undefined; + /** + * Used to custom plugins of the chart. + */ + plugins?: any; + /** + * Width of the chart in non-responsive mode. + * Default value is 300. + */ + width?: number | undefined; + /** + * Height of the chart in non-responsive mode. + * Default value is 150. + */ + height?: number | undefined; +} + +export interface ChartSlots { +} + +export declare type ChartEmits = { + /** + * Callback to invoke when a tab gets expanded. + * @param {ChartSelectEvent} event - Custom select event. + */ + 'select': (event: ChartSelectEvent) => void; + /** + * Callback to invoke when chart is loaded. + * @param {*} chart - Chart instance. + */ + 'loaded': (chart: any) => void; +} + +declare class Chart extends ClassComponent { + /** + * Redraws the graph. + * + * @memberof Chart + */ + refresh: () => void; + /** + * Destroys the graph first and then creates it again. + * + * @memberof Chart + */ + reinit: () => void; + /** + * Returns an HTML string of a legend for that chart. The legend is generated from the legendCallback in the options. + * + * @memberof Chart + */ + generateLegend: () => string | any; + /** + * Returns Chart instance. + * + * @memberof Chart + */ + getChart: () => any; +} + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Chart: GlobalComponentConstructor + } +} + +/** + * + * Chart components are based on Charts.js, an open source HTML5 based charting library. + * + * Helper API; + * + * - [Chart.js](https://www.chartjs.org/) + * + * Demos: + * + * - [Chart](https://www.primefaces.org/primevue/showcase/#/chart) + * + */ +export default Chart; diff --git a/components/chart/Chart.vue b/components/chart/Chart.vue new file mode 100755 index 000000000..7520d5e84 --- /dev/null +++ b/components/chart/Chart.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/components/chart/package.json b/components/chart/package.json new file mode 100644 index 000000000..ff33763d9 --- /dev/null +++ b/components/chart/package.json @@ -0,0 +1,9 @@ +{ + "main": "./chart.cjs.js", + "module": "./chart.esm.js", + "unpkg": "./chart.min.js", + "types": "./Chart.d.ts", + "browser": { + "./sfc": "./Chart.vue" + } +} \ No newline at end of file diff --git a/components/checkbox/Checkbox.css b/components/checkbox/Checkbox.css new file mode 100755 index 000000000..12547a4b6 --- /dev/null +++ b/components/checkbox/Checkbox.css @@ -0,0 +1,17 @@ +.p-checkbox { + display: inline-flex; + cursor: pointer; + user-select: none; + vertical-align: bottom; + position: relative; +} + +.p-checkbox.p-checkbox-disabled { + cursor: default; +} + +.p-checkbox-box { + display: flex; + justify-content: center; + align-items: center; +} diff --git a/components/checkbox/Checkbox.d.ts b/components/checkbox/Checkbox.d.ts new file mode 100755 index 000000000..835074f8a --- /dev/null +++ b/components/checkbox/Checkbox.d.ts @@ -0,0 +1,114 @@ +import { InputHTMLAttributes } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface CheckboxProps { + /** + * Value of the checkbox. + */ + value?: any; + /** + * Value binding of the checkbox. + */ + modelValue?: any; + /** + * Name of the input element. + */ + name?: string | undefined; + /** + * Allows to select a boolean value instead of multiple values. + */ + binary?: boolean; + /** + * When present, it specifies that the element should be disabled. + */ + disabled?: boolean | undefined; + /** + * When present, it specifies that an input field is read-only. + */ + readonly?: boolean | undefined; + /** + * When present, it specifies that the element is required. + */ + required?: boolean | undefined; + /** + * Index of the element in tabbing order. + */ + tabindex?: number | undefined; + /** + * Value in checked state. + */ + trueValue?: any; + /** + * Value in unchecked state. + */ + falseValue?: any; + /** + * Identifier of the underlying input element. + */ + inputId?: string | undefined; + /** + * Style class of the input field. + */ + inputClass?: any | undefined; + /** + * Inline style of the input field. + */ + inputStyle?: any | undefined; + /** + * Uses to pass all properties of the HTMLInputElement to the focusable input element inside the component. + */ + inputProps?: InputHTMLAttributes | undefined; + /** + * Establishes relationships between the component and label(s) where its value should be one or more element IDs. + */ + 'aria-labelledby'?: string | undefined; + /** + * Establishes a string value that labels the component. + */ + 'aria-label'?: string | undefined; +} + +export interface CheckboxSlots { +} + +export declare type CheckboxEmits = { + /** + * Emitted when the page changes. + * @param {*} value - New page value. + */ + 'update:page': (value: any) => void; + /** + * Callback to invoke on value click. + * @param {MouseEvent} event - Browser event. + */ + 'click': (event: MouseEvent) => void; + /** + * Callback to invoke on value change. + * @param {Event} event - Browser event. + */ + 'change': (event: Event) => void; + /** + * Callback to invoke on value change. + * @param {boolean} value - New value. + */ + 'input': (value: boolean) => void; +} + +declare class Checkbox extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Checkbox: GlobalComponentConstructor + } +} + +/** + * + * Checkbox is an extension to standard checkbox element with theming. + * + * Demos: + * + * - [Checkbox](https://www.primefaces.org/primevue/showcase/#/checkbox) + * + */ +export default Checkbox; diff --git a/components/checkbox/Checkbox.spec.js b/components/checkbox/Checkbox.spec.js new file mode 100644 index 000000000..7f68f569b --- /dev/null +++ b/components/checkbox/Checkbox.spec.js @@ -0,0 +1,28 @@ +import { mount } from '@vue/test-utils'; +import Checkbox from './Checkbox.vue'; + +describe('Checkbox.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Checkbox, { + props: { + modelValue: false, + binary: true + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-checkbox.p-component').exists()).toBe(true); + expect(wrapper.find('.p-checkbox-icon.pi.pi-check').exists()).toBe(false); + }); + + it('should exist', async () => { + await wrapper.setProps({ modelValue: true }); + + expect(wrapper.find('.p-checkbox-checked').exists()).toBe(true); + expect(wrapper.find('.p-checkbox-box.p-highlight').exists()).toBe(true); + expect(wrapper.find('.p-checkbox-icon.pi.pi-check').exists()).toBe(true); + }); +}); \ No newline at end of file diff --git a/components/checkbox/Checkbox.vue b/components/checkbox/Checkbox.vue new file mode 100755 index 000000000..bc2a999d9 --- /dev/null +++ b/components/checkbox/Checkbox.vue @@ -0,0 +1,114 @@ + + + diff --git a/components/checkbox/package.json b/components/checkbox/package.json new file mode 100644 index 000000000..d24b36c3e --- /dev/null +++ b/components/checkbox/package.json @@ -0,0 +1,9 @@ +{ + "main": "./checkbox.cjs.js", + "module": "./checkbox.esm.js", + "unpkg": "./checkbox.min.js", + "types": "./Checkbox.d.ts", + "browser": { + "./sfc": "./Checkbox.vue" + } +} \ No newline at end of file diff --git a/components/chip/Chip.d.ts b/components/chip/Chip.d.ts new file mode 100644 index 000000000..20d5648cc --- /dev/null +++ b/components/chip/Chip.d.ts @@ -0,0 +1,60 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface ChipProps { + /** + * Defines the text to display. + */ + label?: string; + /** + * Defines the icon to display. + */ + icon?: string; + /** + * Defines the image to display. + */ + image?: string; + /** + * Whether to display a remove icon. + */ + removable?: boolean; + /** + * Icon of the remove element. + * Default value is 'pi pi-times-circle'. + */ + removeIcon?: string; +} + +export interface ChipSlots { + /** + * Content can easily be customized with the default slot instead of using the built-in modes. + */ + default: () => VNode[]; +} + +export declare type ChipEmits = { + /** + * Callback to invoke when a chip is removed. + * @param {Event} event - Browser event. + */ + 'remove': (event: Event) => void; +} + +declare class Chip extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Chip: GlobalComponentConstructor + } +} + +/** + * + * Chip represents entities using icons, labels and images. + * + * Demos: + * + * - [Chip](https://www.primefaces.org/primevue/showcase/#/chip) + * + */ +export default Chip; diff --git a/components/chip/Chip.spec.js b/components/chip/Chip.spec.js new file mode 100644 index 000000000..751bd284a --- /dev/null +++ b/components/chip/Chip.spec.js @@ -0,0 +1,29 @@ +import { mount } from '@vue/test-utils'; +import Chip from './Chip.vue'; + +describe('Chip.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Chip, { + props: { + label: 'PrimeVue', + icon: 'pi pi-primevue', + removable: true + } + }); + }); + + it('should exists', () => { + expect(wrapper.find('.p-chip.p-component').exists()).toBe(true); + expect(wrapper.find('.p-chip-icon').classes()).toContain('pi-primevue'); + expect(wrapper.find('.p-chip-text').text()).toBe('PrimeVue'); + expect(wrapper.find('.p-chip-remove-icon').exists()).toBe(true); + }); + + it('should close icon work', async () => { + await wrapper.find('.p-chip-remove-icon').trigger('click') + + expect(wrapper.find('.p-chip.p-component').exists()).toBe(false); + }); +}); \ No newline at end of file diff --git a/components/chip/Chip.vue b/components/chip/Chip.vue new file mode 100644 index 000000000..0f25372ae --- /dev/null +++ b/components/chip/Chip.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/components/chip/package.json b/components/chip/package.json new file mode 100644 index 000000000..bfa7d1331 --- /dev/null +++ b/components/chip/package.json @@ -0,0 +1,9 @@ +{ + "main": "./chip.cjs.js", + "module": "./chip.esm.js", + "unpkg": "./chip.min.js", + "types": "./Chip.d.ts", + "browser": { + "./sfc": "./Chip.vue" + } +} \ No newline at end of file diff --git a/components/chips/Chips.d.ts b/components/chips/Chips.d.ts new file mode 100755 index 000000000..4f5f70f31 --- /dev/null +++ b/components/chips/Chips.d.ts @@ -0,0 +1,124 @@ +import { InputHTMLAttributes, VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface ChipsAddEvent { + /** + * Browser event. + */ + originalEvent: Event; + /** + * Added/Removed item value. + */ + value: any; +} + +/** + * @extends ChipsAddEvent + */ +export interface ChipsRemoveEvent extends ChipsAddEvent { } + +export interface ChipsProps { + /** + * Value of the component. + */ + modelValue?: any[]; + /** + * Maximum number of entries allowed. + */ + max?: number | undefined; + /** + * Whether to add an item when the input loses focus. + */ + addOnBlur?: boolean | undefined; + /** + * Whether to allow duplicate values or not. + * Default value is true. + */ + allowDuplicate?: boolean | undefined; + /** + * Separator char to add an item when pressed in addition to the enter key. Currently only possible value is ',' + */ + separator?: string | undefined; + /** + * Identifier of the focus input to match a label defined for the chips. + */ + inputId?: string | undefined; + /** + * Style class of the input field. + */ + inputClass?: any | undefined; + /** + * Inline style of the input field. + */ + inputStyle?: any | undefined; + /** + * Uses to pass all properties of the HTMLInputElement to the focusable input element inside the component. + */ + inputProps?: InputHTMLAttributes | undefined; + /** + * When present, it specifies that the element should be disabled. + */ + disabled?: boolean | undefined; + /** + * Placeholder text for the input. + */ + placeholder?: string | undefined; + /** + * Establishes relationships between the component and label(s) where its value should be one or more element IDs. + */ + 'aria-labelledby'?: string | undefined; + /** + * Establishes a string value that labels the component. + */ + 'aria-label'?: string | undefined; +} + +export interface ChipsSlots { + /** + * Custom chip template. + * @param {Object} scope - chip slot's params. + */ + chip: (scope: { + /** + * Value of the component + */ + value: any; + }) => VNode[]; +} + +export declare type ChipsEmits = { + /** + * Emitted when the value changes. + * @param {*} value - New value. + */ + 'update:modelValue': (value: any[]) => void; + /** + * Callback to invoke when a chip is added. + * @param {ChipsAddEvent} event - Custom add event. + */ + 'add': (event: ChipsAddEvent) => void; + /** + * Callback to invoke when a chip is removed. + * @param {ChipsRemoveEvent} event - Custom remove event. + */ + 'remove': (event: ChipsRemoveEvent) => void; +} + +declare class Chips extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Chips: GlobalComponentConstructor + } +} + +/** + * + * Chips is used to enter multiple values on an input field. + * + * Demos: + * + * - [Chips](https://www.primefaces.org/primevue/showcase/#/chips) + * + */ +export default Chips; diff --git a/components/chips/Chips.spec.js b/components/chips/Chips.spec.js new file mode 100644 index 000000000..719975009 --- /dev/null +++ b/components/chips/Chips.spec.js @@ -0,0 +1,33 @@ +import { mount } from '@vue/test-utils'; +import Chips from './Chips.vue'; + +describe('Chips.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Chips, { + props: { + modelValue: null + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-chips.p-component.p-inputwrapper').exists()).toBe(true); + expect(wrapper.find('ul.p-chips-multiple-container').exists()).toBe(true); + expect(wrapper.find('li.p-chips-input-token').exists()).toBe(true); + }); + + it('should add item', async() => { + const addItem = jest.spyOn(wrapper.vm, 'addItem'); + + await wrapper.vm.addItem({},'PrimeVue', false); + + await wrapper.setProps({ modelValue: ['PrimeVue'] }); + + expect(addItem).toHaveBeenCalled(); + expect(wrapper.findAll('.p-chips-token').length).toBe(1); + expect(wrapper.find('.p-chips-token-label').exists()).toBe(true); + expect(wrapper.find('.p-chips-token-label').text()).toBe('PrimeVue'); + }); +}); \ No newline at end of file diff --git a/components/chips/Chips.vue b/components/chips/Chips.vue new file mode 100755 index 000000000..f38690b84 --- /dev/null +++ b/components/chips/Chips.vue @@ -0,0 +1,298 @@ + + + + + diff --git a/components/chips/package.json b/components/chips/package.json new file mode 100644 index 000000000..f5f0049b0 --- /dev/null +++ b/components/chips/package.json @@ -0,0 +1,9 @@ +{ + "main": "./chips.cjs.js", + "module": "./chips.esm.js", + "unpkg": "./chips.min.js", + "types": "./Chips.d.ts", + "browser": { + "./sfc": "./Chips.vue" + } +} \ No newline at end of file diff --git a/components/colorpicker/ColorPicker.css b/components/colorpicker/ColorPicker.css new file mode 100755 index 000000000..06e52d355 --- /dev/null +++ b/components/colorpicker/ColorPicker.css @@ -0,0 +1,7 @@ +.p-colorpicker-panel .p-colorpicker-color { + background: transparent url("./images/color.png") no-repeat left top; +} + +.p-colorpicker-panel .p-colorpicker-hue { + background: transparent url("./images/hue.png") no-repeat left top; +} \ No newline at end of file diff --git a/components/colorpicker/ColorPicker.d.ts b/components/colorpicker/ColorPicker.d.ts new file mode 100755 index 000000000..39e208560 --- /dev/null +++ b/components/colorpicker/ColorPicker.d.ts @@ -0,0 +1,109 @@ +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type ColorPickerFormatType = 'hex' | 'rgb' | 'hsb' | undefined; + +type ColorPickerAppendToType = 'body' | 'self' | string | undefined | HTMLElement; + +export interface ColorPickerChangeEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Selected color value. + */ + value: any; +} + +export interface ColorPickerProps { + /** + * Value of the component. + */ + modelValue?: any; + /** + * Initial color to display when value is not defined. + * Default value is ff0000. + */ + defaultColor?: any; + /** + * Whether to display as an overlay or not. + */ + inline?: boolean | undefined; + /** + * Format to use in value binding, supported formats are 'hex', 'rgb' and 'hsb'. + * @see ColorPickerFormatType + * Default value is 'hex'. + */ + format?: ColorPickerFormatType; + /** + * When present, it specifies that the component should be disabled. + */ + disabled?: boolean | undefined; + /** + * Index of the element in tabbing order. + */ + tabindex?: string | undefined; + /** + * Whether to automatically manage layering. + * Default value is true. + */ + autoZIndex?: boolean | undefined; + /** + * Base zIndex value to use in layering. + * Default value is 0. + */ + baseZIndex?: number | undefined; + /** + * Style class of the overlay panel. + */ + panelClass?: any; + /** + * A valid query selector or an HTMLElement to specify where the overlay gets attached. Special keywords are 'body' for document body and 'self' for the element itself. + * @see ColorPickerAppendToType + * Default value is 'body'. + */ + appendTo?: ColorPickerAppendToType; +} + +export interface ColorPickerSlots { +} + +export declare type ColorPickerEmits = { + /** + * Emitted when the value changes. + * @param {*} value - New value. + */ + 'update:modelValue': (value: any) => void; + /** + * Callback to invoke when a chip is added. + * @param {ColorPickerChangeEvent} event - Custom add event. + */ + 'change': (event: ColorPickerChangeEvent) => void; + /** + * Callback to invoke when input is cleared by the user. + */ + 'show': () => void; + /** + * Callback to invoke when input is cleared by the user. + */ + 'hide': () => void; +} + +declare class ColorPicker extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + ColorPicker: GlobalComponentConstructor + } +} + +/** + * + * ColorPicker is an input component to select a color. + * + * Demos: + * + * - [ColorPicker](https://www.primefaces.org/primevue/showcase/#/colorpicker) + * + */ +export default ColorPicker; diff --git a/components/colorpicker/ColorPicker.spec.js b/components/colorpicker/ColorPicker.spec.js new file mode 100644 index 000000000..8f9b27e93 --- /dev/null +++ b/components/colorpicker/ColorPicker.spec.js @@ -0,0 +1,59 @@ +import { mount } from '@vue/test-utils'; +import PrimeVue from '@/components/config/PrimeVue'; +import ColorPicker from './ColorPicker.vue'; + +describe('ColorPicker.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(ColorPicker, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true + } + }, + props: { + modelValue: null + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-colorpicker.p-component').exists()).toBe(true); + expect(wrapper.find('.p-colorpicker-preview.p-inputtext').exists()).toBe(true); + }); + + it('should input click triggered', async() => { + const input = wrapper.find('.p-colorpicker-preview.p-inputtext'); + const onInputClick = jest.spyOn(wrapper.vm, 'onInputClick'); + + await input.trigger('click'); + + expect(onInputClick).toHaveBeenCalled(); + expect(wrapper.find('.p-colorpicker-panel').exists()).toBe(true); + expect(wrapper.find('.p-colorpicker-color-selector').exists()).toBe(true); + expect(wrapper.find('.p-colorpicker-hue').exists()).toBe(true); + }); + + it('should mouse events triggered', async() => { + const input = wrapper.find('.p-colorpicker-preview.p-inputtext'); + + await input.trigger('click'); + + const onColorMousedown = jest.spyOn(wrapper.vm, 'onColorMousedown'); + const onHueMousedown = jest.spyOn(wrapper.vm, 'onHueMousedown'); + const event = { pageX: 100, pageY: 120, preventDefault: () => {}}; + const event2 = { pageX: 70, pageY: 20, preventDefault: () => {}}; + + wrapper.vm.onColorMousedown(event); + + expect(onColorMousedown).toHaveBeenCalled(); + expect(wrapper.find('.p-colorpicker-preview.p-inputtext').element.style.backgroundColor).not.toBe('rgb(255, 0, 0)'); + + wrapper.vm.onHueMousedown(event2); + + expect(onHueMousedown).toHaveBeenCalled(); + expect(wrapper.find('.p-colorpicker-preview.p-inputtext').element.style.backgroundColor).not.toBe('rgb(255, 0, 0)'); + }); +}); \ No newline at end of file diff --git a/components/colorpicker/ColorPicker.vue b/components/colorpicker/ColorPicker.vue new file mode 100755 index 000000000..2167700f0 --- /dev/null +++ b/components/colorpicker/ColorPicker.vue @@ -0,0 +1,670 @@ + + + + + diff --git a/components/colorpicker/images/color.png b/components/colorpicker/images/color.png new file mode 100755 index 000000000..561cdd9c5 Binary files /dev/null and b/components/colorpicker/images/color.png differ diff --git a/components/colorpicker/images/hue.png b/components/colorpicker/images/hue.png new file mode 100755 index 000000000..8efa25257 Binary files /dev/null and b/components/colorpicker/images/hue.png differ diff --git a/components/colorpicker/package.json b/components/colorpicker/package.json new file mode 100644 index 000000000..d016adfb4 --- /dev/null +++ b/components/colorpicker/package.json @@ -0,0 +1,9 @@ +{ + "main": "./colorpicker.cjs.js", + "module": "./colorpicker.esm.js", + "unpkg": "./colorpicker.min.js", + "types": "./ColorPicker.d.ts", + "browser": { + "./sfc": "./ColorPicker.vue" + } +} \ No newline at end of file diff --git a/components/column/Column.d.ts b/components/column/Column.d.ts new file mode 100755 index 000000000..371a4d0f2 --- /dev/null +++ b/components/column/Column.d.ts @@ -0,0 +1,488 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; +import { VirtualScrollerLoaderOptions } from '../virtualscroller'; + +type ColumnFieldType = string | ((item: any) => string) | undefined; + +type ColumnSelectionModeType = 'single' | 'multiple' | undefined; + +type ColumnAlignFrozenType = 'left' | 'right' | undefined; + +export interface ColumnFilterModelType { + /** + * Value of filterModel. + */ + value: any; + /** + * Match mode of filterModel. + */ + matchMode: string; +} + +export interface ColumnFilterMatchModeOptions { + [key: string]: string; +} + +export interface ColumnLoadingOptions extends VirtualScrollerLoaderOptions { + /** + * Cell index + */ + cellIndex: number; + /** + * Whether the cell is first. + */ + cellFirst: boolean; + /** + * Whether the cell is last. + */ + cellLast: boolean; + /** + * Whether the cell is even. + */ + cellEven: boolean; + /** + * Whether the item is odd. + */ + cellOdd: boolean; + /** + * Column instance + */ + column: Column; + /** + * Column field + */ + field: string; +} + +export interface ColumnProps { + /** + * Identifier of a column if field property is not defined. + */ + columnKey?: string | undefined; + /** + * Property represented by the column. + * @see ColumnFieldType + */ + field?: ColumnFieldType; + /** + * Property name to use in sorting, defaults to field. + * @see ColumnFieldType + */ + sortField?: ColumnFieldType; + /** + * Property name to use in filtering, defaults to field. + * @see ColumnFieldType + */ + filterField?: ColumnFieldType; + /** + * Type of data. It's value is related to PrimeVue.filterMatchModeOptions config. + */ + dataType?: string | undefined; + /** + * Defines if a column is sortable. + */ + sortable?: boolean | undefined; + /** + * Header content of the column. + */ + header?: string | undefined; + /** + * Footer content of the column. + */ + footer?: string | undefined; + /** + * Inline style of header, body and footer cells. + */ + style?: any; + /** + * Style class of header, body and footer cells. + */ + class?: any; + /** + * Inline style of the column header. + */ + headerStyle?: any; + /** + * Style class of the column header. + */ + headerClass?: any; + /** + * Inline style of the column body. + */ + bodyStyle?: any; + /** + * Style class of the column body. + */ + bodyClass?: any; + /** + * Inline style of the column footer. + */ + footerStyle?: any; + /** + * Style class of the column footer. + */ + footerClass?: any; + /** + * Whether to display the filter overlay. + * Default value is true. + */ + showFilterMenu?: boolean | undefined; + /** + * When enabled, match all and match any operator selector is displayed. + * Default value is true. + */ + showFilterOperator?: boolean | undefined; + /** + * Displays a button to clear the column filtering. + * Default value is true. + */ + showClearButton?: boolean | undefined; + /** + * Displays a button to apply the column filtering. + * Default value is true. + */ + showApplyButton?: boolean | undefined; + /** + * Whether to show the match modes selector. + * Default value is true. + */ + showFilterMatchModes?: boolean | undefined; + /** + * When enabled, a button is displayed to add more rules. + * Default value is true. + */ + showAddButton?: boolean | undefined; + /** + * An array of label-value pairs to override the global match mode options. + */ + filterMatchModeOptions?: ColumnFilterMatchModeOptions[]; + /** + * Maximum number of constraints for a column filter. + * Default value is 2. + */ + maxConstraints?: number | undefined; + /** + * Whether to exclude from global filtering or not. + */ + excludeGlobalFilter?: boolean | undefined; + /** + * Inline style of the column filter header in row filter display. + */ + filterHeaderStyle?: any; + /** + * Style class of the column filter header in row filter display. + */ + filterHeaderClass?: any; + /** + * Inline style of the column filter overlay. + */ + filterMenuStyle?: any; + /** + * Style class of the column filter overlay. + */ + filterMenuClass?: any; + /** + * Defines column based selection mode, options are 'single' and 'multiple'. + * @see ColumnSelectionModeType + */ + selectionMode?: ColumnSelectionModeType; + /** + * Displays an icon to toggle row expansion. + */ + expander?: boolean | undefined; + /** + * Number of columns to span for grouping. + */ + colspan?: number | undefined; + /** + * Number of rows to span for grouping. + */ + rowspan?: number | undefined; + /** + * Whether this column displays an icon to reorder the rows. + */ + rowReorder?: boolean | undefined; + /** + * Icon of the drag handle to reorder rows. + * Default value is 'pi pi-bars'. + */ + rowReorderIcon?: string | undefined; + /** + * Defines if the column itself can be reordered with dragging. + */ + reorderableColumn?: boolean | undefined; + /** + * When enabled, column displays row editor controls. + */ + rowEditor?: boolean | undefined; + /** + * Whether the column is fixed in horizontal scrolling. + */ + frozen?: boolean | undefined; + /** + * Position of a frozen column, valid values are left and right. + * @see ColumnAlignFrozenType + * Default value is 'left'. + */ + alignFrozen?: ColumnAlignFrozenType; + /** + * Whether the column is included in data export. + */ + exportable?: boolean | undefined; + /** + * Custom export header of the column to be exported as CSV. + */ + exportHeader?: string | undefined; + /** + * Custom export footer of the column to be exported as CSV. + */ + exportFooter?: string | undefined; + /** + * Defines the filtering algorithm to use when searching the options. + */ + filterMatchMode?: string | undefined; + /** + * Whether the column is rendered. + */ + hidden?: boolean | undefined; +} + +export interface ColumnSlots { + /** + * Custom body template. + * @param {Object} scope - body slot's params. + */ + body: (scope: { + /** + * Row data. + */ + data: any; + /** + * Row node data. + */ + node: any; + /** + * Column node. + */ + column: Column; + /** + * Column field. + */ + field: string; + /** + * Row index. + */ + index: number; + /** + * Whether the row is frozen. + */ + frozenRow: boolean; + /** + * Callback function + */ + editorInitCallback: () => void; + }) => VNode[]; + /** + * Custom header template. + * @param {Object} scope - header slot's params. + */ + header: (scope: { + /** + * Column node. + */ + column: Column; + }) => VNode[]; + /** + * Custom footer template. + * @param {Object} scope - footer slot's params. + */ + footer: (scope: { + /** + * Column node. + */ + column: Column; + }) => VNode[]; + /** + * Custom editor template. + * @param {Object} scope - editor slot's params. + */ + editor: (scope: { + /** + * Row data. + */ + data: any; + /** + * Column node. + */ + column: Column; + /** + * Column field. + */ + field: string; + /** + * Row index. + */ + index: number; + /** + * Whether the row is frozen. + */ + frozenRow: boolean; + /** + * Callback function + */ + editorSaveCallback: () => void; + /** + * Callback function + */ + editorCancelCallback: () => void; + }) => VNode[]; + /** + * Custom filter template. + * @param {Object} scope - filter slot's params. + */ + filter: (scope: { + /** + * Column field. + */ + field: string; + /** + * Filter metadata + * @see ColumnFilterModelType + */ + filterModel: ColumnFilterModelType; + /** + * Callback function + */ + filterCallback: () => void; + }) => VNode[]; + /** + * Custom filter header template. + * @param {Object} scope - filter header slot's params. + */ + filterheader: (scope: { + /** + * Column field. + */ + field: string; + /** + * Filter metadata + * @see ColumnFilterModelType + */ + filterModel: ColumnFilterModelType; + /** + * Callback function + */ + filterCallback: () => void; + }) => VNode[]; + /** + * Custom filter footer template. + * @param {Object} scope - filter footer slot's params. + */ + filterfooter: (scope: { + /** + * Column field. + */ + field: string; + /** + * Filter metadata + * @see ColumnFilterModelType + */ + filterModel: ColumnFilterModelType; + /** + * Callback function + */ + filterCallback: () => void; + }) => VNode[]; + /** + * Custom filter clear template. + * @param {Object} scope - filter clear slot's params. + */ + filterclear: (scope: { + /** + * Column field. + */ + field: string; + /** + * Filter metadata + * @see ColumnFilterModelType + */ + filterModel: ColumnFilterModelType; + /** + * Callback function + */ + filterCallback: () => void; + }) => VNode[]; + /** + * Custom filter apply template. + * @param {Object} scope - filter apply slot's params. + */ + filterapply: (scope: { + /** + * Column field. + */ + field: string; + /** + * Filter metadata + * @see ColumnFilterModelType + */ + filterModel: ColumnFilterModelType; + /** + * Callback function + */ + filterCallback: () => void; + }) => VNode[]; + /** + * Custom loading template. + * @param {Object} scope - loading slot's params. + */ + loading: (scope: { + /** + * Row data. + */ + data: any; + /** + * Column node. + */ + column: Column; + /** + * Column field. + */ + field: string; + /** + * Row index. + */ + index: number; + /** + * Whether the row is frozen. + */ + frozenRow: boolean; + /** + * Loading options. + * @see ColumnLoadingOptions + */ + loadingOptions: ColumnLoadingOptions; + }) => VNode[]; +} + +export declare type ColumnEmits = { +} + +declare class Column extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Column: GlobalComponentConstructor + } +} + +/** + * + * Column is a helper component for DataTable and TreeTable. + * + * Demos: + * + * - [DataTable](https://www.primefaces.org/primevue/showcase/#/datatable) + * - [TreeTable](https://www.primefaces.org/primevue/showcase/#/treetable) + * + */ +export default Column; diff --git a/components/column/Column.vue b/components/column/Column.vue new file mode 100755 index 000000000..c2be3e2ae --- /dev/null +++ b/components/column/Column.vue @@ -0,0 +1,186 @@ + diff --git a/components/column/package.json b/components/column/package.json new file mode 100644 index 000000000..c24d869dc --- /dev/null +++ b/components/column/package.json @@ -0,0 +1,9 @@ +{ + "main": "./column.cjs.js", + "module": "./column.esm.js", + "unpkg": "./column.min.js", + "types": "./Column.d.ts", + "browser": { + "./sfc": "./Column.vue" + } +} \ No newline at end of file diff --git a/components/columngroup/ColumnGroup.d.ts b/components/columngroup/ColumnGroup.d.ts new file mode 100755 index 000000000..fd0b09684 --- /dev/null +++ b/components/columngroup/ColumnGroup.d.ts @@ -0,0 +1,35 @@ +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type ColumnGroupType = 'header' | 'footer' | undefined; + +export interface ColumnGroupProps { + /** + * Type of column group + */ + type?: ColumnGroupType; +} + +export interface ColumnGroupSlots { +} + +export declare type ColumnGroupEmits = { +} + +declare class ColumnGroup extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + ColumnGroup: GlobalComponentConstructor + } +} + +/** + * + * ColumnGroup is a helper component to create grouped header and footer on DataTable. + * + * Demos: + * + * - [ColumnGroup](https://www.primefaces.org/primevue/showcase/#/datatable/colgroup) + * + */ +export default ColumnGroup; diff --git a/components/columngroup/ColumnGroup.vue b/components/columngroup/ColumnGroup.vue new file mode 100755 index 000000000..679c41fba --- /dev/null +++ b/components/columngroup/ColumnGroup.vue @@ -0,0 +1,14 @@ + diff --git a/components/columngroup/package.json b/components/columngroup/package.json new file mode 100644 index 000000000..a7b4358bf --- /dev/null +++ b/components/columngroup/package.json @@ -0,0 +1,9 @@ +{ + "main": "./columngroup.cjs.js", + "module": "./columngroup.esm.js", + "unpkg": "./columngroup.min.js", + "types": "./ColumnGroup.d.ts", + "browser": { + "./sfc": "./ColumnGroup.vue" + } +} \ No newline at end of file diff --git a/components/common/Common.css b/components/common/Common.css new file mode 100755 index 000000000..aa19ab3ff --- /dev/null +++ b/components/common/Common.css @@ -0,0 +1,168 @@ +.p-component, .p-component * { + box-sizing: border-box; +} + +.p-hidden { + display: none; +} + +.p-hidden-space { + visibility: hidden; +} + +.p-hidden-accessible { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} + +.p-hidden-accessible input, +.p-hidden-accessible select { + transform: scale(0); +} + +.p-reset { + margin: 0; + padding: 0; + border: 0; + outline: 0; + text-decoration: none; + font-size: 100%; + list-style: none; +} + +.p-disabled, .p-disabled * { + cursor: default !important; + pointer-events: none; + user-select: none; +} + +.p-component-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.p-overflow-hidden { + overflow: hidden; +} + +.p-unselectable-text { + user-select: none; +} + +.p-scrollbar-measure { + width: 100px; + height: 100px; + overflow: scroll; + position: absolute; + top: -9999px; +} + +@-webkit-keyframes p-fadein { + 0% { opacity: 0; } + 100% { opacity: 1; } +} +@keyframes p-fadein { + 0% { opacity: 0; } + 100% { opacity: 1; } +} + +input[type="button"], +input[type="submit"], +input[type="reset"], +input[type="file"]::-webkit-file-upload-button, +button { + border-radius: 0; +} + +.p-link { + text-align: left; + background-color: transparent; + margin: 0; + padding: 0; + border: none; + cursor: pointer; + user-select: none; +} + +.p-link:disabled { + cursor: default; +} + +/* Non vue overlay animations */ +.p-connected-overlay { + opacity: 0; + transform: scaleY(0.8); + transition: transform .12s cubic-bezier(0, 0, 0.2, 1), opacity .12s cubic-bezier(0, 0, 0.2, 1); +} + +.p-connected-overlay-visible { + opacity: 1; + transform: scaleY(1); +} + +.p-connected-overlay-hidden { + opacity: 0; + transform: scaleY(1); + transition: opacity .1s linear; +} + +/* Vue based overlay animations */ +.p-connected-overlay-enter-from { + opacity: 0; + transform: scaleY(0.8); +} + +.p-connected-overlay-leave-to { + opacity: 0; +} + +.p-connected-overlay-enter-active { + transition: transform .12s cubic-bezier(0, 0, 0.2, 1), opacity .12s cubic-bezier(0, 0, 0.2, 1); +} + +.p-connected-overlay-leave-active { + transition: opacity .1s linear; +} + +/* Toggleable Content */ +.p-toggleable-content-enter-from, +.p-toggleable-content-leave-to { + max-height: 0; +} + +.p-toggleable-content-enter-to, +.p-toggleable-content-leave-from { + max-height: 1000px; +} + +.p-toggleable-content-leave-active { + overflow: hidden; + transition: max-height 0.45s cubic-bezier(0, 1, 0, 1); +} + +.p-toggleable-content-enter-active { + overflow: hidden; + transition: max-height 1s ease-in-out; +} + +.p-sr-only { + border: 0; + clip: rect(1px, 1px, 1px, 1px); + clip-path: inset(50%); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; + word-wrap: normal !important; +} diff --git a/components/confirmationeventbus/ConfirmationEventBus.js b/components/confirmationeventbus/ConfirmationEventBus.js new file mode 100644 index 000000000..844a1b111 --- /dev/null +++ b/components/confirmationeventbus/ConfirmationEventBus.js @@ -0,0 +1,3 @@ +import {EventBus} from 'primevue/utils'; + +export default EventBus(); \ No newline at end of file diff --git a/components/confirmationeventbus/package.json b/components/confirmationeventbus/package.json new file mode 100644 index 000000000..84b4ed833 --- /dev/null +++ b/components/confirmationeventbus/package.json @@ -0,0 +1,5 @@ +{ + "main": "./confirmationeventbus.cjs.js", + "module": "./confirmationeventbus.esm.js", + "unpkg": "./confirmationeventbus.min.js" + } \ No newline at end of file diff --git a/components/confirmationoptions/ConfirmationOptions.d.ts b/components/confirmationoptions/ConfirmationOptions.d.ts new file mode 100644 index 000000000..0d5b867f9 --- /dev/null +++ b/components/confirmationoptions/ConfirmationOptions.d.ts @@ -0,0 +1,70 @@ +type ConfirmationPositionType = 'center' | 'top' | 'bottom' | 'left' | 'right' | 'topleft' | 'topright' | 'bottomleft' | 'bottomright' | undefined; + +export interface ConfirmationOptions { + /** + * Element to align the overlay. + */ + target?: HTMLElement | undefined; + /** + * Header text of the dialog. + */ + header?: string | undefined; + /** + * Message of the confirmation. + */ + message?: string | undefined; + /** + * Optional key to match the key of the confirmation, useful to target a specific confirm dialog instance. + */ + group?: string | undefined; + /** + * Position of the dialog, options are 'center', 'top', 'bottom', 'left', 'right', 'topleft', 'topright', 'bottomleft' or 'bottomright'. + * @see ConfirmationPositionType + * Default value is 'center'. + */ + position?: ConfirmationPositionType; + /** + * Icon to display next to the message. + */ + icon?: string | undefined; + /** + * Whether background scroll should be blocked when dialog is visible. + */ + blockScroll?: boolean | undefined; + /** + * Callback to execute when action is confirmed. + */ + accept?: () => void; + /** + * Callback to execute when action is rejected. + */ + reject?: () => void; + /** + * Label of the accept button. Defaults to PrimeVue Locale configuration. + */ + acceptLabel?: string | undefined; + /** + * Label of the reject button. Defaults to PrimeVue Locale configuration. + */ + rejectLabel?: string | undefined; + /** + * Icon of the accept button. + */ + acceptIcon?: string | undefined; + /** + * Icon of the reject button. + */ + rejectIcon?: string | undefined; + /** + * Style class of the accept button. + */ + acceptClass?: string | undefined; + /** + * Style class of the reject button. + */ + rejectClass?: string | undefined; + /** + * Element to receive the focus when the dialog gets visible, valid values are "accept" and "reject". + */ + defaultFocus?: string | undefined; +} diff --git a/components/confirmationoptions/package.json b/components/confirmationoptions/package.json new file mode 100644 index 000000000..af9a8d180 --- /dev/null +++ b/components/confirmationoptions/package.json @@ -0,0 +1,3 @@ +{ + "types": "./ConfirmationOptions.d.ts" +} diff --git a/components/confirmationservice/ConfirmationService.d.ts b/components/confirmationservice/ConfirmationService.d.ts new file mode 100644 index 000000000..0fec0231a --- /dev/null +++ b/components/confirmationservice/ConfirmationService.d.ts @@ -0,0 +1,29 @@ +import Vue, { Plugin } from 'vue'; +import { ConfirmationOptions } from '../confirmationoptions'; + +declare const plugin: Plugin; +export default plugin; + +export interface ConfirmationServiceMethods { + /** + * Displays the dialog using the confirmation object options. + * @param {ConfirmationOptions} options - Confirmation Object + */ + require(options: ConfirmationOptions): void; + /** + * Hides the dialog without invoking accept or reject callbacks. + */ + close(): void; +} + +declare module 'vue/types/vue' { + interface Vue { + $confirm: ConfirmationServiceMethods; + } +} + +declare module '@vue/runtime-core' { + interface ComponentCustomProperties { + $confirm: ConfirmationServiceMethods; + } +} diff --git a/components/confirmationservice/ConfirmationService.js b/components/confirmationservice/ConfirmationService.js new file mode 100644 index 000000000..7f314435c --- /dev/null +++ b/components/confirmationservice/ConfirmationService.js @@ -0,0 +1,17 @@ +import ConfirmationEventBus from 'primevue/confirmationeventbus'; +import {PrimeVueConfirmSymbol} from 'primevue/useconfirm'; + +export default { + install: (app) => { + const ConfirmationService = { + require: (options) => { + ConfirmationEventBus.emit('confirm', options); + }, + close: () => { + ConfirmationEventBus.emit('close'); + } + }; + app.config.globalProperties.$confirm = ConfirmationService; + app.provide(PrimeVueConfirmSymbol, ConfirmationService); + } +}; \ No newline at end of file diff --git a/components/confirmationservice/package.json b/components/confirmationservice/package.json new file mode 100644 index 000000000..c6f800733 --- /dev/null +++ b/components/confirmationservice/package.json @@ -0,0 +1,6 @@ +{ + "main": "./confirmationservice.cjs.js", + "module": "./confirmationservice.esm.js", + "unpkg": "./confirmationservice.min.js", + "types": "./ConfirmationService.d.ts" +} \ No newline at end of file diff --git a/components/confirmdialog/ConfirmDialog.d.ts b/components/confirmdialog/ConfirmDialog.d.ts new file mode 100644 index 000000000..f2131a330 --- /dev/null +++ b/components/confirmdialog/ConfirmDialog.d.ts @@ -0,0 +1,71 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; +import { ConfirmationOptions } from '../confirmationoptions'; + +export interface ConfirmDialogBreakpoints { + /** + * Breakpoint for responsive mode. + * + * Example: + * + * + * + * Result: + * + * @media screen and (max-width: ${breakpoint[key]}) { + * .p-dialog[attributeSelector] { + * width: ${breakpoint[value]} !important; + * } + * } + */ + [key: string]: string; +} + +export interface ConfirmDialogProps { + /** + * Optional key to match the key of the confirmation, useful to target a specific confirm dialog instance. + */ + group?: string | undefined; + /** + * Object literal to define widths per screen size. + * @see ConfirmDialogBreakpoints + */ + breakpoints?: ConfirmDialogBreakpoints; +} + +export interface ConfirmDialogSlots { + /** + * Custom message template. + * @param {Object} scope - message slot's params. + */ + message: (scope: { + message: ConfirmationOptions; + }) => VNode[]; +} + +export declare type ConfirmDialogEmits = { +} + +declare class ConfirmDialog extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + ConfirmDialog: GlobalComponentConstructor + } +} + +/** + * + * ConfirmDialog uses a Dialog UI that is integrated with the Confirmation API. + * + * Helper API: + * + * - Confirmation API + * - ConfirmationService + * + * Demos: + * + * - [ConfirmDialog](https://www.primefaces.org/primevue/showcase/#/confirmdialog) + * + */ +export default ConfirmDialog; diff --git a/components/confirmdialog/ConfirmDialog.spec.js b/components/confirmdialog/ConfirmDialog.spec.js new file mode 100644 index 000000000..a516f2cbe --- /dev/null +++ b/components/confirmdialog/ConfirmDialog.spec.js @@ -0,0 +1,168 @@ +import { mount } from '@vue/test-utils'; +import PrimeVue from '@/components/config/PrimeVue'; +import ConfirmDialog from './ConfirmDialog.vue'; + +describe('ConfirmDialog', () => { + it('should exist', async() => { + const wrapper = mount(ConfirmDialog, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true, + transition: false + }, + }, + data() { + return { + confirmation: { + message: 'Are you sure you want to proceed?', + header: 'Confirmation', + icon: 'pi pi-exclamation-triangle' + } + } + } + }); + + await wrapper.setData({ visible: true }); + + expect(wrapper.find('.p-dialog-mask .p-dialog.p-component').exists()).toBe(true); + expect(wrapper.find('.p-dialog-title').text()).toBe('Confirmation'); + expect(wrapper.find('.p-confirm-dialog-message').text()).toBe('Are you sure you want to proceed?'); + + await wrapper.vm.reject(); + + expect(wrapper.find('.p-dialog-mask .p-dialog.p-component').exists()).toBe(false); + }); + + it('should dialog trigger the accept function', async() => { + const wrapper = mount(ConfirmDialog, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true, + transition: false + } + }, + data() { + return { + confirmation: { + message: 'Are you sure you want to proceed?', + header: 'Confirmation', + icon: 'pi pi-exclamation-triangle', + accept: () => { + console.log('accept') + }, + reject: () => { + console.log('reject'); + + } + } + } + } + }); + + const acceptTriggered = jest.spyOn(wrapper.componentVM.confirmation, 'accept'); + + await wrapper.setData({ visible: true }); + + const CDAcceptBtn = wrapper.find('.p-confirm-dialog-accept'); + + await CDAcceptBtn.trigger('click'); + + expect(acceptTriggered).toBeCalled(); + }); + + it('should dialog trigger the reject function', async() => { + const wrapper = mount(ConfirmDialog, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true, + transition: false + } + }, + data() { + return { + confirmation: { + message: 'Are you sure you want to proceed?', + header: 'Confirmation', + icon: 'pi pi-exclamation-triangle', + accept: () => { + console.log('accept') + }, + reject: () => { + console.log('reject'); + + } + } + } + } + }); + + const rejectTriggered = jest.spyOn(wrapper.componentVM.confirmation, 'reject'); + + await wrapper.setData({ visible: true }); + + const CDRejectBtn = wrapper.find('.p-confirm-dialog-reject'); + + await CDRejectBtn.trigger('click'); + + expect(rejectTriggered).toBeCalled(); + }); + + it('should dialog close button work', async() => { + const wrapper = mount(ConfirmDialog, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true, + transition: false + } + }, + data() { + return { + confirmation: { + message: 'Are you sure you want to proceed?', + header: 'Confirmation', + icon: 'pi pi-exclamation-triangle' + } + } + } + }); + + await wrapper.setData({ visible: true }); + + const dialogCloseBtn = wrapper.find('.p-dialog-header-close'); + + await dialogCloseBtn.trigger('click'); + + expect(wrapper.find('.p-dialog-mask .p-dialog.p-component').exists()).toBe(false); + }); + + it('should position work', async () => { + const wrapper = mount(ConfirmDialog, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true, + transition: false + } + }, + data() { + return { + confirmation: { + group: 'positionDialog', + message: 'Do you want to delete this record?', + header: 'Delete Confirmation', + icon: 'pi pi-info-circle', + position: 'bottom' + } + } + } + }); + + await wrapper.setData({ visible: true }); + + expect(wrapper.find('.p-dialog-mask.p-dialog-bottom').exists()).toBe(true); + }); +}); \ No newline at end of file diff --git a/components/confirmdialog/ConfirmDialog.vue b/components/confirmdialog/ConfirmDialog.vue new file mode 100644 index 000000000..5dee6d61b --- /dev/null +++ b/components/confirmdialog/ConfirmDialog.vue @@ -0,0 +1,126 @@ + + + diff --git a/components/confirmdialog/package.json b/components/confirmdialog/package.json new file mode 100644 index 000000000..fd1b774df --- /dev/null +++ b/components/confirmdialog/package.json @@ -0,0 +1,9 @@ +{ + "main": "./confirmdialog.cjs.js", + "module": "./confirmdialog.esm.js", + "unpkg": "./confirmdialog.min.js", + "types": "./ConfirmDialog.d.ts", + "browser": { + "./sfc": "./ConfirmDialog.vue" + } +} \ No newline at end of file diff --git a/components/confirmpopup/ConfirmPopup.d.ts b/components/confirmpopup/ConfirmPopup.d.ts new file mode 100644 index 000000000..a304c8712 --- /dev/null +++ b/components/confirmpopup/ConfirmPopup.d.ts @@ -0,0 +1,47 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; +import { ConfirmationOptions } from '../confirmationoptions'; + +export interface ConfirmPopupProps { + /** + * Optional key to match the key of the confirmation, useful to target a specific confirm dialog instance. + */ + group?: string; +} + +export interface ConfirmPopupSlots { + /** + * Custom message template. + * @param {Object} scope - message slot's params. + */ + message: (scope: { + message: ConfirmationOptions; + }) => VNode[]; +} + +export declare type ConfirmPopupEmits = { +} + +declare class ConfirmPopup extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + ConfirmPopup: GlobalComponentConstructor + } +} + +/** + * + * ConfirmPopup displays a confirmation overlay displayed relatively to its target. + * + * Helper API: + * + * - Confirmation API + * - ConfirmationService + * + * Demos: + * + * - [ConfirmPopup](https://www.primefaces.org/primevue/showcase/#/confirmpopup) + * + */ +export default ConfirmPopup; diff --git a/components/confirmpopup/ConfirmPopup.vue b/components/confirmpopup/ConfirmPopup.vue new file mode 100644 index 000000000..cd363cdb4 --- /dev/null +++ b/components/confirmpopup/ConfirmPopup.vue @@ -0,0 +1,301 @@ + + + + + diff --git a/components/confirmpopup/package.json b/components/confirmpopup/package.json new file mode 100644 index 000000000..7595d1d69 --- /dev/null +++ b/components/confirmpopup/package.json @@ -0,0 +1,9 @@ +{ + "main": "./confirmpopup.cjs.js", + "module": "./confirmpopup.esm.js", + "unpkg": "./confirmpopup.min.js", + "types": "./ConfirmPopup.d.ts", + "browser": { + "./sfc": "./ConfirmPopup.vue" + } +} \ No newline at end of file diff --git a/components/contextmenu/ContextMenu.d.ts b/components/contextmenu/ContextMenu.d.ts new file mode 100755 index 000000000..3cda7cfca --- /dev/null +++ b/components/contextmenu/ContextMenu.d.ts @@ -0,0 +1,98 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; +import { MenuItem } from '../menuitem'; + +type ContextMenuAppendTo = 'body' | 'self' | string | undefined | HTMLElement; + +export interface ContextMenuProps { + /** + * An array of menuitems. + */ + model?: MenuItem[] | undefined; + /** + * A valid query selector or an HTMLElement to specify where the overlay gets attached. + * @see ContextMenuAppendTo + * Default value is 'body' + */ + appendTo?: ContextMenuAppendTo; + /** + * Whether to automatically manage layering. + * Default value is true. + */ + autoZIndex?: boolean | undefined; + /** + * Base zIndex value to use in layering. + * Default value is 0. + */ + baseZIndex?: number | undefined; + /** + * Attaches the menu to document instead of a particular item. + */ + global?: boolean | undefined; + /** + * Whether to apply 'router-link-active-exact' class if route exactly matches the item path. + * Default value is true. + */ + exact?: boolean | undefined; +} + +export interface ContextMenuSlots { + /** + * Custom item template. + * @param {Object} scope - item slot's params. + */ + item: (scope: { + /** + * Menuitem instance + */ + item: MenuItem; + }) => VNode[]; +} + +export declare type ContextMenuEmits = { +} + +declare class ContextMenu extends ClassComponent { + /** + * Toggles the visibility of the menu. + * @param {Event} event - Browser event. + * + * @memberof ContextMenu + */ + toggle: (event: Event) => void; + /** + * Shows the menu. + * @param {Event} event - Browser event. + * + * @memberof ContextMenu + */ + show: (event: Event) => void; + /** + * Hides the menu. + * + * @memberof ContextMenu + */ + hide: () => void; +} + +declare module '@vue/runtime-core' { + interface GlobalComponents { + ContextMenu: GlobalComponentConstructor + } +} + +/** + * + * ContextMenu displays an overlay menu on right click of its target. Note that components like DataTable has special integration with ContextMenu. + * Refer to documentation of the individual documentation of the with context menu support. + * + * Helper API: + * + * - [MenuItem](https://www.primefaces.org/primevue/showcase/#/menumodel) + * + * Demos: + * + * - [ContextMenu](https://www.primefaces.org/primevue/showcase/#/contextmenu) + * + */ +export default ContextMenu; diff --git a/components/contextmenu/ContextMenu.spec.js b/components/contextmenu/ContextMenu.spec.js new file mode 100644 index 000000000..3977d22b6 --- /dev/null +++ b/components/contextmenu/ContextMenu.spec.js @@ -0,0 +1,175 @@ +import { mount } from '@vue/test-utils'; +import PrimeVue from '@/components/config/PrimeVue'; +import ContextMenu from './ContextMenu.vue'; + +describe('ContextMenu.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(ContextMenu, { + global: { + plugins: [PrimeVue], + stubs: { + 'router-link': true, + teleport: true + } + }, + props: { + model: [ + { + label:'File', + icon:'pi pi-fw pi-file', + items:[ + { + label:'New', + icon:'pi pi-fw pi-plus', + items:[ + { + label:'Bookmark', + icon:'pi pi-fw pi-bookmark' + }, + { + label:'Video', + icon:'pi pi-fw pi-video' + }, + ] + }, + { + label:'Delete', + icon:'pi pi-fw pi-trash' + }, + { + separator:true + }, + { + label:'Export', + icon:'pi pi-fw pi-external-link' + } + ] + }, + { + label:'Edit', + icon:'pi pi-fw pi-pencil', + items:[ + { + label:'Left', + icon:'pi pi-fw pi-align-left' + }, + { + label:'Right', + icon:'pi pi-fw pi-align-right' + }, + { + label:'Center', + icon:'pi pi-fw pi-align-center' + }, + { + label:'Justify', + icon:'pi pi-fw pi-align-justify' + }, + + ] + }, + { + label:'Users', + icon:'pi pi-fw pi-user', + items:[ + { + label:'New', + icon:'pi pi-fw pi-user-plus', + + }, + { + label:'Delete', + icon:'pi pi-fw pi-user-minus', + + }, + { + label:'Search', + icon:'pi pi-fw pi-users', + items:[ + { + label:'Filter', + icon:'pi pi-fw pi-filter', + items:[ + { + label:'Print', + icon:'pi pi-fw pi-print' + } + ] + }, + { + icon:'pi pi-fw pi-bars', + label:'List' + } + ] + } + ] + }, + { + label:'Events', + icon:'pi pi-fw pi-calendar', + items:[ + { + label:'Edit', + icon:'pi pi-fw pi-pencil', + items:[ + { + label:'Save', + icon:'pi pi-fw pi-calendar-plus' + }, + { + label:'Delete', + icon:'pi pi-fw pi-calendar-minus' + }, + ] + }, + { + label:'Archieve', + icon:'pi pi-fw pi-calendar-times', + items:[ + { + label:'Remove', + icon:'pi pi-fw pi-calendar-minus' + } + ] + } + ] + }, + { + separator:true + }, + { + label:'Quit', + icon:'pi pi-fw pi-power-off' + } + ] + } + }); + }); + + it('should exist', async() => { + const event = { pageX: 100, pageY: 120, preventDefault: () => {}, stopPropagation: () => {}}; + const show = jest.spyOn(wrapper.vm, 'show'); + + wrapper.vm.show(event); + await wrapper.setData({ visible: true }); + + expect(show).toHaveBeenCalled(); + expect(wrapper.find('.p-contextmenu.p-component').exists()).toBe(true); + expect(wrapper.findAll('.p-menuitem').length).toBe(5); + expect(wrapper.findAll('.p-menuitem-text')[0].text()).toBe('File'); + }); + + it('should hide menu', async() => { + const hide = jest.spyOn(wrapper.vm, 'hide'); + + await wrapper.setData({ visible: true }); + + wrapper.vm.hide(); + await wrapper.setData({ visible: false }); + + expect(hide).toHaveBeenCalled(); + expect(wrapper.find('.p-contextmenu.p-component').exists()).toBe(false); + }); +}); \ No newline at end of file diff --git a/components/contextmenu/ContextMenu.vue b/components/contextmenu/ContextMenu.vue new file mode 100755 index 000000000..642a3a51a --- /dev/null +++ b/components/contextmenu/ContextMenu.vue @@ -0,0 +1,264 @@ + + + + + diff --git a/components/contextmenu/ContextMenuSub.vue b/components/contextmenu/ContextMenuSub.vue new file mode 100755 index 000000000..16acc5957 --- /dev/null +++ b/components/contextmenu/ContextMenuSub.vue @@ -0,0 +1,165 @@ + + + diff --git a/components/contextmenu/package.json b/components/contextmenu/package.json new file mode 100644 index 000000000..0a0614126 --- /dev/null +++ b/components/contextmenu/package.json @@ -0,0 +1,9 @@ +{ + "main": "./contextmenu.cjs.js", + "module": "./contextmenu.esm.js", + "unpkg": "./contextmenu.min.js", + "types": "./ContextMenu.d.ts", + "browser": { + "./sfc": "./ContextMenu.vue" + } +} \ No newline at end of file diff --git a/components/datatable/BodyCell.vue b/components/datatable/BodyCell.vue new file mode 100755 index 000000000..c343ddefc --- /dev/null +++ b/components/datatable/BodyCell.vue @@ -0,0 +1,417 @@ + + + diff --git a/components/datatable/ColumnFilter.vue b/components/datatable/ColumnFilter.vue new file mode 100644 index 000000000..c6402b6b7 --- /dev/null +++ b/components/datatable/ColumnFilter.vue @@ -0,0 +1,522 @@ + + + diff --git a/components/datatable/DataTable.d.ts b/components/datatable/DataTable.d.ts new file mode 100755 index 000000000..66cdd777b --- /dev/null +++ b/components/datatable/DataTable.d.ts @@ -0,0 +1,1076 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor, Nullable } from '../ts-helpers'; +import Column from '../column'; +import { VirtualScrollerProps } from '../virtualscroller'; + +type DataTablePaginatorPositionType = 'top' | 'bottom' | 'both' | undefined; + +type DataTableSortFieldType = string | ((item: any) => string) | undefined; + +type DataTableDataKeyType = string | ((item: any) => string) | undefined; + +type DataTableMultiSortMetaType = DataTableSortMeta[] | undefined | null; + +type DataTableSortOrderType = 1 | 0 | -1 | undefined | null; + +type DataTableSortModeType = 'single' | 'multiple' | undefined; + +type DataTableFilterMatchModeType = 'startsWith' | 'contains' | 'notContains' | 'endsWith' | 'equals' | 'notEquals' | 'in' | 'lt' | 'lte' | 'gt' | 'gte' | 'between' | 'dateIs' | 'dateIsNot' | 'dateBefore' | 'dateAfter' | undefined; + +type DataTableFilterDisplayType = 'menu' | 'row' | undefined; + +type DataTableSelectionModeType = 'single' | 'multiple' | undefined; + +type DataTableCompareSelectionBy = 'equals' | 'deepEquals' | undefined; + +type DataTableColumnResizeModeType = 'fit' | 'expand' | undefined; + +type DataTableRowGroupModeType = 'subheader' | 'rowspan' | undefined; + +type DataTableStateStorageType = 'session' | 'local' | undefined; + +type DataTableEditModeType = 'cell' | 'row' | undefined; + +type DataTableScrollDirectionType = 'vertical' | 'horizontal' | 'both' | undefined; + +type DataTableScrollHeightType = 'flex' | string | undefined; + +type DataTableResponsiveLayoutType = 'stack' | 'scroll' | undefined; + +export interface DataTableExportFunctionOptions { + /** + * Row data + */ + data: any; + /** + * Column Field + */ + field: string; +} + +export interface DataTableFilterMetaData { + /** + * Filter value + */ + value: any; + /** + * Filter match mode + * @see DataTableFilterMatchModeType + */ + matchMode: string; +} + +export interface DataTableOperatorFilterMetaData { + /** + * Filter operator + */ + operator: string; + /** + * Array of filter meta datas. + * @see DataTableFilterMetaData + */ + constraints: DataTableFilterMetaData[]; +} + +export interface DataTableFilterMeta { + /** + * Filter keys + * @see DataTableFilterMetaData + */ + [key: string]: string | DataTableFilterMetaData | DataTableOperatorFilterMetaData; +} + +export interface DataTableSortMeta { + /** + * Column field + */ + field: string; + /** + * Column sort order + */ + order: DataTableSortOrderType; +} + +export interface DataTableExpandedRows { + [key: string]: boolean; +} + +export interface DataTableEditingRows { + [key: string]: boolean; +} + +export interface DataTableExportCSVOptions { + /** + * Whether to export only selection data. + */ + selectionOnly: boolean; +} + +export interface DataTableSortEvent { + /** + * Browser event. + */ + originalEvent: Event; + /** + * Index of first record + */ + first: number; + /** + * Number of rows to display in new page + */ + rows: number; + /** + * Field to sort against + * @see DataTableSortFieldType + */ + sortField: DataTableSortFieldType; + /** + * Sort order as integer + * @see DataTableSortOrderType + */ + sortOrder: DataTableSortOrderType; + /** + * MultiSort metadata + * @see DataTableMultiSortMetaType + */ + multiSortMeta: DataTableMultiSortMetaType; + /** + * Collection of active filters + * @see DataTableFilterMeta + */ + filters: DataTableFilterMeta; + /** + * Match modes per field + * @see DataTableFilterMatchModeType + */ + filterMatchModes: DataTableFilterMatchModeType; +} + +/** + * @extends DataTableSortEvent + */ +export interface DataTablePageEvent extends DataTableSortEvent { + /** + * New page number + */ + page: number; + /** + * Total page count + */ + pageCount: number; +} + +/** + * @extends DataTableSortEvent + */ + export interface DataTableFilterEvent extends DataTableSortEvent { + /** + * Filtered collection (non-lazy only) + */ + filteredValue: any; +} + +export interface DataTableRowClickEvent { + /** + * Browser event. + */ + originalEvent: Event; + /** + * Selected row data. + */ + data: any; + /** + * Row index. + */ + index: number; +} + +/** + * @extends DataTableRowClickEvent + */ +export interface DataTableRowDoubleClickEvent extends DataTableRowClickEvent { } + +/** + * @extends DataTableRowClickEvent + */ +export interface DataTableRowContextMenuEvent extends DataTableRowClickEvent { } + +export interface DataTableRowSelectEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Selected row data + */ + data: any; + /** + * Row index + */ + index: number; + /** + * Type of the selection, valid values are 'row', 'radio' or 'checkbox'. + */ + type: string; +} + +/** + * @extends DataTableRowSelectEvent + */ +export interface DataTableRowUnselectEvent extends DataTableRowSelectEvent { } + +export interface DataTableRowSelectAllEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Selected dataset + */ + data: any; +} + +export interface DataTableRowUnselectAllEvent { + /** + * Browser event + */ + originalEvent: Event; +} + +export interface DataTableSelectAllChangeEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Whether all data is selected. + */ + checked: boolean; +} + +export interface DataTableColumnResizeEndEvent { + /** + * DOM element of the resized column. + */ + element: HTMLElement; + /** + * Change in column width + */ + delta: any; +} + +export interface DataTableColumnReorderEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Index of the dragged column + */ + dragIndex: number; + /** + * Index of the dropped column + */ + dropIndex: number; +} + +export interface DataTableRowReorderEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Index of the dragged row + */ + dragIndex: number; + /** + * Index of the dropped row + */ + dropIndex: number; + /** + * Reordered value + */ + value: any[]; +} + +export interface DataTableRowExpandEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Expanded row data + */ + data: any[]; +} + +/** + * @extends DataTableRowExpandEvent + */ +export interface DataTableRowCollapseEvent extends DataTableRowExpandEvent { } + +export interface DataTableCellEditInitEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Row data to edit. + */ + data: any; + /** + * Field name of the row data. + */ + field: string; + /** + * Index of the row data to edit. + */ + index: number; +} + +/** + * @extends DataTableCellEditInitEvent + */ +export interface DataTableCellEditCancelEvent extends DataTableCellEditInitEvent { } + +export interface DataTableCellEditCompleteEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Row data to edit. + */ + data: any; + /** + * New row data after editing. + */ + newData: any; + /** + * Field value of row data to edit. + */ + value: any; + /** + * Field value of new row data after editing. + */ + newValue: any; + /** + * Field name of the row data. + */ + field: string; + /** + * Index of the row data to edit. + */ + index: number; + /** + * Type of completion such as 'enter', 'outside' or 'tab'. + */ + type: string; +} + +export interface DataTableRowEditInitEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Row data to edit. + */ + data: any; + /** + * New row data after editing. + */ + newData: any; + /** + * Field name of the row data. + */ + field: string; + /** + * Index of the row data to edit. + */ + index: number; +} + +/** + * @extends DataTableRowEditInitEvent + */ +export interface DataTableRowEditSaveEvent extends DataTableRowEditInitEvent { } + +/** + * @extends DataTableRowEditCancelEvent + */ +export interface DataTableRowEditCancelEvent extends DataTableRowEditInitEvent { } + +export interface DataTableStateEvent { + /** + * Index of first record + */ + first: number; + /** + * Number of rows to display in new page + */ + rows: number; + /** + * Field to sort against + */ + sortField: string; + /** + * Sort order as integer + * @see DataTableSortOrderType + */ + sortOrder: DataTableSortOrderType; + /** + * MultiSort metadata + * @see DataTableMultiSortMetaType + */ + multiSortMeta: DataTableMultiSortMetaType; + /** + * Collection of active filters + * @see DataTableFilterMeta + */ + filters: DataTableFilterMeta; + /** + * Comma separated list of column widths + */ + columWidths: string[]; + /** + * Order of the columns + */ + columnOrder: string[]; + /** + * Instances of rows in expanded state + * @see DataTableExpandedRows + */ + expandedRows: any[] | DataTableExpandedRows; + /** + * Keys of rows in expanded state + */ + expandedRowKeys: any[]; + /** + * Instances of rows in expanded state + */ + expandedRowGroups: any[] | DataTableExpandedRows; + /** + * Selected rows + */ + selection: any[] | any; + /** + * Keys of selected rows + */ + selectionKeys: any[]; +} + +export interface DataTableProps { + /** + * An array of objects to display. + */ + value?: any[] | undefined; + /** + * Name of the field that uniquely identifies the a record in the data. + * @see DataTableDataKeyType + */ + dataKey?: DataTableDataKeyType; + /** + * Number of rows to display per page. + */ + rows?: number | undefined; + /** + * Index of the first row to be displayed. + * Default value is 0. + */ + first?: number | undefined; + /** + * Number of total records, defaults to length of value when not defined. + */ + totalRecords?: number | undefined; + /** + * When specified as true, enables the pagination. + */ + paginator?: boolean | undefined; + /** + * Position of the paginator, options are 'top','bottom' or 'both'. + * @see DataTablePaginatorPositionType + * Default value is 'bottom'. + */ + paginatorPosition?: DataTablePaginatorPositionType; + /** + * Whether to show it even there is only one page. + * Default value is true. + */ + alwaysShowPaginator?: boolean | undefined; + /** + * Template of the paginator. It can be customized using the template property using the predefined keys, default value is 'FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown'. Here are the available elements that can be placed inside a paginator in any order. + * + * - FirstPageLink + * - PrevPageLink + * - PageLinks + * - NextPageLink + * - LastPageLink + * - RowsPerPageDropdown + * - JumpToPageDropdown + * - JumpToPageInput + * - CurrentPageReport + */ + paginatorTemplate?: string | undefined; + /** + * Number of page links to display. + * Default value is 5. + */ + pageLinkSize?: number | undefined; + /** + * Array of integer values to display inside rows per page dropdown. + */ + rowsPerPageOptions?: number[] | undefined; + /** + * Template of the current page report element. It displays information about the pagination state. Default value is ({currentPage} of {totalPages}) whereas available placeholders are the following; + * + * - {currentPage} + * - {totalPages} + * - {rows} + * - {first} + * - {last} + * - {totalRecords} + */ + currentPageReportTemplate?: string | undefined; + /** + * Defines if data is loaded and interacted with in lazy manner. + */ + lazy?: boolean | undefined; + /** + * Displays a loader to indicate data load is in progress. + */ + loading?: boolean | undefined; + /** + * The icon to show while indicating data load is in progress. + * Default value is 'pi pi-spinner'. + */ + loadingIcon?: string | undefined; + /** + * Property name or a getter function of a row data used for sorting by default + * @see DataTableSortFieldType + */ + sortField?: DataTableSortFieldType | undefined; + /** + * Order to sort the data by default. + */ + sortOrder?: number | undefined; + /** + * Default sort order of an unsorted column. + * Default value is 1. + */ + defaultSortOrder?: number | undefined; + /** + * An array of SortMeta objects to sort the data by default in multiple sort mode. + * @see DataTableMultiSortMetaType + */ + multiSortMeta?: DataTableMultiSortMetaType; + /** + * Defines whether sorting works on single column or on multiple columns. + * @see DataTableSortModeType + * Default value is 'single'. + */ + sortMode?: DataTableSortModeType; + /** + * When enabled, columns can have an un-sorted state. + */ + removableSort?: boolean | undefined; + /** + * Filters object with key-value pairs to define the filters. + * @see DataTableFilterMeta + */ + filters?: DataTableFilterMeta; + /** + * Layout of the filter elements, valid values are 'row' and 'menu'. + * @see DataTableFilterDisplayType + */ + filterDisplay?: DataTableFilterDisplayType; + /** + * Fields for global filter + */ + globalFilterFields?: string[] | undefined; + /** + * Locale to use in filtering. The default locale is the host environment's current locale. + */ + filterLocale?: string | undefined; + /** + * Selected row in single mode or an array of values in multiple mode. + */ + selection?: any[] | any | undefined; + /** + * Specifies the selection mode, valid values are 'single' and 'multiple'. + * @see DataTableSelectionModeType + */ + selectionMode?: DataTableSelectionModeType; + /** + * Algorithm to define if a row is selected, valid values are 'equals' that compares by reference and 'deepEquals' that compares all fields. + * @see DataTableCompareSelectionBy + * Default value is 'deepEquals'. + */ + compareSelectionBy?: DataTableCompareSelectionBy; + /** + * Defines whether metaKey is requred or not for the selection. When true metaKey needs to be pressed to select or unselect an item and + * when set to false selection of each item can be toggled individually. On touch enabled devices, metaKeySelection is turned off automatically. + * Default value is 'true'. + */ + metaKeySelection?: boolean | undefined; + /** + * Enables context menu integration. + */ + contextMenu?: boolean | undefined; + /** + * Selected row instance with the ContextMenu. + */ + contextMenuSelection?: any | any[] | undefined; + /** + * Whether all data is selected. + */ + selectAll?: Nullable; + /** + * When enabled, background of the rows change on hover. + */ + rowHover?: boolean | undefined; + /** + * Character to use as the csv separator. + * Default value is ','. + */ + csvSeparator?: string | undefined; + /** + * Name of the exported file. + * Default value is 'download'. + */ + exportFilename?: string | undefined; + /** + * Custom function to export data. + * @see DataTableExportFunctionOptions + */ + exportFunction?(options: DataTableExportFunctionOptions): any; + /** + * Whether the cell widths scale according to their content or not. Does not apply to scrollable tables. + */ + autoLayout?: boolean | undefined; + /** + * When enabled, columns can be resized using drag and drop. + */ + resizableColumns?: boolean | undefined; + /** + * Defines whether the overall table width should change on column resize, valid values are 'fit' and 'expand'. + * @see DataTableColumnResizeModeType + * Default value is 'fit'. + */ + columnResizeMode?: DataTableColumnResizeModeType; + /** + * When enabled, columns can be reordered using drag and drop. + */ + reorderableColumns?: boolean | undefined; + /** + * A collection of row data display as expanded. + * @see DataTableExpandedRows + */ + expandedRows?: any[] | DataTableExpandedRows; + /** + * Icon of the row toggler to display the row as expanded. + * Default value is 'pi-chevron-down'. + */ + expandedRowIcon?: string | undefined; + /** + * Icon of the row toggler to display the row as collapsed. + * Default value is 'pi-chevron-right'. + */ + collapsedRowIcon?: string | undefined; + /** + * Defines the row group mode, valid options are 'subheader' and 'rowspan'. + * @see DataTableRowGroupModeType + */ + rowGroupMode?: DataTableRowGroupModeType; + /** + * One or more field names to use in row grouping. + */ + groupRowsBy?: string[] | string | undefined; + /** + * Whether the row groups can be expandable. + */ + expandableRowGroups?: boolean | undefined; + /** + * An array of group field values whose groups would be rendered as expanded. + * @see DataTableExpandedRows + */ + expandedRowGroups?: any[] | DataTableExpandedRows; + /** + * Defines where a stateful table keeps its state, valid values are 'session' for sessionStorage and 'local' for localStorage. + * @see DataTableStateStorageType + * Default value is 'session'. + */ + stateStorage?: DataTableStateStorageType; + /** + * Unique identifier of a stateful table to use in state storage. + */ + stateKey?: string | undefined; + /** + * Defines the incell editing mode, valid options are 'cell' and 'row'. + * @see DataTableEditModeType + */ + editMode?: DataTableEditModeType; + /** + * A collection of rows to represent the current editing data in row edit mode. + * @see DataTableEditingRows + */ + editingRows?: any[] | DataTableEditingRows; + /** + * A function that takes the row data as a parameter and returns a string to apply a particular class for the row. + * + */ + rowClass?:(data: any) => object | string | undefined; + /** + * A function that takes the row data as a parameter and returns the inline style for the corresponding row. + */ + rowStyle?: any; + /** + * When specified, enables horizontal and/or vertical scrolling. + */ + scrollable?: boolean | undefined; + /** + * Height of the scroll viewport in fixed pixels or the 'flex' keyword for a dynamic size. + * @see DataTableScrollHeightType + */ + scrollHeight?: DataTableScrollHeightType; + /** + * Orientation of the scrolling, options are 'vertical', 'horizontal' and 'both'. + * @see DataTableScrollDirectionType + * Default value is 'vertical'. + */ + scrollDirection?: DataTableScrollDirectionType; + /** + * Whether to use the virtualScroller feature. The properties of VirtualScroller component can be used like an object in it. + * Note: Currently only vertical orientation mode is supported. + * @see VirtualScroller.VirtualScrollerProps + */ + virtualScrollerOptions?: VirtualScrollerProps; + /** + * Items of the frozen part in scrollable DataTable. + */ + frozenValue?: any[] | undefined; + /** + * Defines the responsive mode, valid options are 'stack' and 'scroll'. + * @see DataTableResponsiveLayoutType + * Default value is 'stack'. + */ + responsiveLayout?: DataTableResponsiveLayoutType; + /** + * The breakpoint to define the maximum width boundary when using stack responsive layout. + * Default value is '960px'. + */ + breakpoint?: string | undefined; + /** + * Whether to show grid lines between cells. + */ + showGridlines?: boolean | undefined; + /** + * Whether to displays rows with alternating colors. + */ + stripedRows?: boolean | undefined; + /** + * Inline style of the table element. + */ + tableStyle?: any; + /** + * Style class of the table element. + */ + tableClass?: any; +} + +export interface DataTableSlots { + /** + * Custom header template. + */ + header: () => VNode[]; + /** + * Custom footer template. + */ + footer: () => VNode[]; + /** + * Custom paginator start template. + */ + paginatorstart: () => VNode[]; + /** + * Custom paginator end template. + */ + paginatorend: () => VNode[]; + /** + * Custom empty template. + */ + empty: () => VNode[]; + /** + * Custom group header template. + * @param {Object} scope - group header slot's params. + */ + groupheader: (scope: { + /** + * Row data + */ + data: any; + /** + * Row index + */ + index: number; + }) => VNode[]; + /** + * Custom group footer template. + * @param {Object} scope - group footer slot's params. + */ + groupfooter: (scope: { + /** + * Row data + */ + data: any; + /** + * Row index + */ + index: number; + }) => VNode[]; + /** + * Custom loading template. + */ + loading: () => VNode[]; + /** + * Custom expansion template. + * @param {Object} scope - expansion slot's params. + */ + expansion: (scope: { + /** + * Row data + */ + data: any; + /** + * Row index + */ + index: number; + }) => VNode[]; +} + +export declare type DataTableEmits = { + /** + * Emitted when the first changes. + * @param {number} value - New value. + */ + 'update:first': (value: number) => void; + /** + * Emitted when the rows changes. + * @param {number} value - New value. + */ + 'update:rows': (value: number) => void; + /** + * Emitted when the sortField changes. + * @param {string} value - New value. + */ + 'update:sortField': (value: string) => void; + /** + * Emitted when the sortOrder changes. + * @param {number | undefined} value - New value. + */ + 'update:sortOrder': (value: number | undefined) => void; + /** + * Emitted when the multiSortMeta changes. + * @param {DataTableMultiSortMetaType} value - New value. + */ + 'update:multiSortMeta': (value: DataTableMultiSortMetaType) => void; + /** + * Emitted when the selection changes. + * @param {*} value - New value. + */ + 'update:selection': (value: any[] | any | undefined) => void; + /** + * Emitted when the contextMenuSelection changes. + * @param {*} value - New value. + */ + 'update:contextMenuSelection': (value: any[] | any | undefined ) => void; + /** + * Emitted when the expandedRows changes. + * @param {DataTableExpandedRows} value - New value. + */ + 'update:expandedRows': (value: any[] | DataTableExpandedRows) => void; + /** + * Emitted when the expandedRowGroups changes. + * @param {DataTableExpandedRows} value - New value. + */ + 'update:expandedRowGroups': (value: any[] | DataTableExpandedRows) => void; + /** + * Emitted when the filters changes. + * @param {DataTableFilterMeta} value - New value. + */ + 'update:filters': (value: DataTableFilterMeta) => void; + /** + * Emitted when the editingRows changes. + * @param {DataTableEditingRows} value - New value. + */ + 'update:editingRows': (value: any[] | DataTableEditingRows) => void; + /** + * Callback to invoke on pagination. Sort and Filter information is also available for lazy loading implementation. + * @param {DataTablePageEvent} event - Custom page event. + */ + 'page': (event: DataTablePageEvent) => void; + /** + * Callback to invoke on sort. Page and Filter information is also available for lazy loading implementation. + * @param {DataTableSortEvent} event - Custom sort event. + */ + 'sort': (event: DataTableSortEvent) => void; + /** + * Event to emit after filtering, not triggered in lazy mode. + * @param {DataTableFilterEvent} event - Custom filter event. + */ + 'filter': (event: DataTableFilterEvent) => void; + /** + * Callback to invoke after filtering, sorting, pagination and cell editing to pass the rendered value. + * @param {*} value - Value displayed by the table. + */ + 'value-change': (value: any[]) => void; + /** + * Callback to invoke when a row is clicked. + * @param {DataTableRowClickEvent} event - Custom row click event. + */ + 'row-click': (event: DataTableRowClickEvent) => void; + /** + * Callback to invoke when a row is double clicked. + * @param {DataTableRowDoubleClickEvent} event - Custom row double click event. + */ + 'row-dblclick': (event: DataTableRowDoubleClickEvent) => void; + /** + * Callback to invoke when a row is selected with a ContextMenu. + * @param {DataTableRowContextMenuEvent} event - Custom row context menu event. + */ + 'row-contextmenu': (event: DataTableRowContextMenuEvent) => void; + /** + * Callback to invoke when a row is selected. + * @param {DataTableRowSelectEvent} event - Custom row select event. + */ + 'row-select': (event: DataTableRowSelectEvent) => void; + /** + * Fired when header checkbox is checked. + * @param {DataTableRowSelectAllEvent} event - Custom row select all event. + */ + 'row-select-all': (event: DataTableRowSelectAllEvent) => void; + /** + * Fired when header checkbox is unchecked. + * @param {DataTableRowUnselectAllEvent} event - Custom row unselect all event. + */ + 'row-unselect-all': (event: DataTableRowUnselectAllEvent) => void; + /** + * Callback to invoke when a row is unselected. + * @param {DataTableRowUnselectEvent} event - Custom row unselect event. + */ + 'row-unselect': (event: DataTableRowUnselectEvent) => void; + /** + * Callback to invoke when all data is selected. + * @param {DataTableSelectAllChangeEvent} event - Custom select all change event. + */ + 'select-all-change': (event: DataTableSelectAllChangeEvent) => void; + /** + * Callback to invoke when a column is resized. + * @param {DataTableColumnResizeEndEvent} - Custom column resize event. + */ + 'column-resize-end': (event: DataTableColumnResizeEndEvent) => void; + /** + * Callback to invoke when a column is reordered. + * @param {DataTableColumnReorderEvent} event - Custom column reorder event. + */ + 'column-reorder': (event: DataTableColumnReorderEvent) => void; + /** + * Callback to invoke when a row is reordered. + * @param {DataTableRowReorderEvent} event - Custom row reorder event. + */ + 'row-reorder': (event: DataTableRowReorderEvent) => void; + /** + * Callback to invoke when a row is expanded. + * @param {DataTableRowExpandEvent} event - Custom row expand event. + */ + 'row-expand': (event: DataTableRowExpandEvent) => void; + /** + * Callback to invoke when a row is collapsed. + * @param {DataTableRowCollapseEvent} event - Custom row collapse event. + */ + 'row-collapse': (event: DataTableRowCollapseEvent) => void; + /** + * Callback to invoke when a row group is expanded. + * @param {DataTableRowExpandEvent} event - Custom row expand event. + */ + 'rowgroup-expand': (event: DataTableRowExpandEvent) => void; + /** + * Callback to invoke when a row group is collapsed. + * @param {DataTableRowCollapseEvent} event - Custom row collapse event. + */ + 'rowgroup-collapse': (event: DataTableRowCollapseEvent) => void; + /** + * Callback to invoke when cell edit is initiated. + * @param {DataTableCellEditInitEvent} event - Custom cell edit init. + */ + 'cell-edit-init': (event: DataTableCellEditInitEvent) => void; + /** + * Callback to invoke when cell edit is completed. + * @param {DataTableCellEditCompleteEvent} event - Custom cell edit complete event. + */ + 'cell-edit-complete': (event: DataTableCellEditCompleteEvent) => void; + /** + * Callback to invoke when cell edit is cancelled with escape key. + * @param {DataTableCellEditCancelEvent} event - Custom cell edit cancel event. + */ + 'cell-edit-cancel': (event: DataTableCellEditCancelEvent) => void; + /** + * Callback to invoke when row edit is initiated. + * @param {DataTableRowEditInitEvent} event - Custom row edit init event. + */ + 'row-edit-init': (event: DataTableRowEditInitEvent) => void; + /** + * Callback to invoke when row edit is saved. + * @param {DataTableRowEditSaveEvent} event - Custom row edit save event. + */ + 'row-edit-save': (event: DataTableRowEditSaveEvent) => void; + /** + * Callback to invoke when row edit is cancelled. + * @param {DataTableRowEditCancelEvent} event - Custom row edit cancel event. + */ + 'row-edit-cancel': (event: DataTableRowEditCancelEvent) => void; + /** + * Invoked when a stateful table saves the state. + * @param {DataTableStateEvent} event - Custom state event. + */ + 'state-restore': (event: DataTableStateEvent) => void; + /** + * Invoked when a stateful table restores the state. + * @param {DataTableStateEvent} event - Custom state event. + */ + 'state-save': (event: DataTableStateEvent) => void; +} + +declare class DataTable extends ClassComponent { + /** + * Exports the data to CSV format. + * @param {DataTableExportCSVOptions} [options] - Export options. + * @param {Object[]} [data] - Custom exportable data. This param can be used on lazy dataTable. + */ + exportCSV: (options?: DataTableExportCSVOptions, data?: any[]) => void; +} + +declare module '@vue/runtime-core' { + interface GlobalComponents { + DataTable: GlobalComponentConstructor + } +} + +/** + * + * DataTable displays data in tabular format. + * + * Helper Components: + * + * - Column + * - ColumnGroup + * + * Demos: + * + * - [DataTable](https://www.primefaces.org/primevue/showcase/#/datatable) + * - [Edit](https://www.primefaces.org/primevue/showcase/#/datatable/edit) + * - [Sort](https://www.primefaces.org/primevue/showcase/#/datatable/sort) + * - [Filter](https://www.primefaces.org/primevue/showcase/#/datatable/filter) + * etc. + * + */ +export default DataTable; diff --git a/components/datatable/DataTable.spec.js b/components/datatable/DataTable.spec.js new file mode 100644 index 000000000..94bea83f5 --- /dev/null +++ b/components/datatable/DataTable.spec.js @@ -0,0 +1,1413 @@ +import { mount } from '@vue/test-utils'; +import DataTable from './DataTable.vue'; +import ColumnGroup from '@/components/columngroup/ColumnGroup.vue'; +import Row from '@/components/row/Row.vue'; +import Column from '@/components/column/Column.vue'; +import Button from '@/components/button/Button.vue'; +import InputText from '@/components/inputtext/InputText.vue'; +import {FilterMatchMode} from 'primevue/api'; + +window.URL.createObjectURL = function() {}; + +const smallData = [ + { + "id": "1000", + "code": "vbb124btr", + "name": "Game Controller" + }, + { + "id": "1001", + "code": "nvklal433", + "name": "Black Watch" + }, + { + "id": "1002", + "code": "zz21cz3c1", + "name": "Blue Band" + } +]; + +const data = [ + { + "id": "1000", + "code": "vbb124btr", + "name": "Game Controller" + }, + { + "id": "1001", + "code": "nvklal433", + "name": "Black Watch" + }, + { + "id": "1002", + "code": "zz21cz3c1", + "name": "Blue Band" + }, + { + "id": "1003", + "code": "244wgerg2", + "name": "Blue T-Shirt" + }, + { + "id": "1004", + "code": "h456wer53", + "name": "Bracelet" + }, + { + "id": "1005", + "code": "cm230f032", + "name": "Gaming Set" + }, + { + "id": "1006", + "code": "bib36pfvm", + "name": "Chakra Bracelet" + }, + { + "id": "1007", + "code": "mbvjkgip5", + "name": "Galaxy Earrings" + }, + { + "id": "1008", + "code": "f230fh0g3", + "name": "Bamboo Watch" + }, + { + "id": "1009", + "code": "av2231fwg", + "name": "Brown Purse" + } +]; + +describe('DataTable.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(DataTable, { + global: { + components: { + Column, + Button + } + }, + props: { + value: smallData, + expandedRows: [], + paginatorTemplate: 'CurrentPageReport FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown', + rowsPerPageOptions: [5, 6, 7], + currentPageReportTemplate: 'Showing {first} to {last} of {totalRecords}' + }, + slots: { + default: ` + + + + `, + header: `Header Templating`, + footer: `Footer Templating`, + expansion: `Expansion Templating`, + empty: `Empty Templating`, + paginatorstart: `Paginator Start Templating`, + paginatorend: `Paginator End Templating` + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-datatable.p-component').exists()).toBe(true); + expect(wrapper.find('.p-datatable-wrapper').exists()).toBe(true); + expect(wrapper.find('table.p-datatable-table').exists()).toBe(true); + expect(wrapper.find('thead.p-datatable-thead').exists()).toBe(true); + expect(wrapper.find('tbody.p-datatable-tbody').exists()).toBe(true); + }); + + it('should have basic demo', () => { + expect(wrapper.findAll('.p-column-header-content').length).toEqual(3); + const tbody = wrapper.find('.p-datatable-tbody'); + + expect(tbody.findAll('tr').length).toEqual(3); + + const rows = tbody.findAll('tr'); + + expect(rows[0].findAll('td').length).toEqual(3); + }); + + + // table templating + it('should have header template', () => { + expect(wrapper.find('.p-datatable-header').exists()).toBe(true); + expect(wrapper.find('.p-datatable-header').text()).toBe('Header Templating'); + }); + + it('should have footer template', () => { + expect(wrapper.find('.p-datatable-footer').exists()).toBe(true); + expect(wrapper.find('.p-datatable-footer').text()).toBe('Footer Templating'); + }); + + it('should have expansion template', async () => { + await wrapper.setProps({ expandedRows: [smallData[0]] }); + + expect(wrapper.find('tr.p-datatable-row-expansion').exists()).toBe(true); + expect(wrapper.find('tr.p-datatable-row-expansion').text()).toBe('Expansion Templating'); + }); + + it('should have empty templating', async () => { + await wrapper.setProps({ value: [] }); + + expect(wrapper.find('tr.p-datatable-emptymessage').exists()).toBe(true); + expect(wrapper.find('tr.p-datatable-emptymessage').text()).toBe('Empty Templating'); + }); + + it('should have paginatorstart templating', async () => { + await wrapper.setProps({ value: data, paginator: true, rows: 5 }); + + expect(wrapper.find('.p-paginator-left-content').exists()).toBe(true); + expect(wrapper.find('.p-paginator-left-content').text()).toBe('Paginator Start Templating'); + }); + + it('should have paginatorend templating', async () => { + await wrapper.setProps({ value: data, paginator: true, rows: 5 }); + + expect(wrapper.find('.p-paginator-right-content').exists()).toBe(true); + expect(wrapper.find('.p-paginator-right-content').text()).toBe('Paginator End Templating'); + }); + + + // column templating + + + // column grouping + it('should exist', () => { + wrapper = mount({ + components: { + DataTable, + ColumnGroup, + Row, + Column + }, + template: ` + + + + + + + + + + + + + + + + + + + + + + + + + + `, + data() { + return { + sales: null + } + }, + created() { + this.sales = [ + {product: 'Bamboo Watch', lastYearSale: 51, thisYearSale: 40, lastYearProfit: 54406, thisYearProfit: 43342}, + {product: 'Black Watch', lastYearSale: 83, thisYearSale: 9, lastYearProfit: 423132, thisYearProfit: 312122}, + {product: 'Blue Band', lastYearSale: 38, thisYearSale: 5, lastYearProfit: 12321, thisYearProfit: 8500} + ]; + }, + methods: { + formatCurrency(value) { + return value.toLocaleString('en-US', {style: 'currency', currency: 'USD'}); + } + }, + computed: { + lastYearTotal() { + let total = 0; + for(let sale of this.sales) { + total += sale.lastYearProfit; + } + + return this.formatCurrency(total); + }, + thisYearTotal() { + let total = 0; + for(let sale of this.sales) { + total += sale.thisYearProfit; + } + + return this.formatCurrency(total); + } + } + }); + + expect(wrapper.find('.p-datatable').classes()).toContain('p-datatable-grouped-header'); + expect(wrapper.find('.p-datatable').classes()).toContain('p-datatable-grouped-footer'); + + const headerRows = wrapper.findAll('.p-datatable-thead > tr'); + + expect(headerRows.length).toEqual(3); + + expect(headerRows[0].findAll('th')[0].attributes().rowspan).toEqual('3'); + expect(headerRows[0].findAll('th')[1].attributes().colspan).toEqual('4'); + expect(headerRows[0].findAll('th')[0].text()).toEqual('Product'); + expect(headerRows[0].findAll('th')[1].text()).toEqual('Sale Rate'); + + expect(headerRows[1].findAll('th')[0].attributes().colspan).toEqual('2'); + expect(headerRows[1].findAll('th')[1].attributes().colspan).toEqual('2'); + expect(headerRows[1].findAll('th')[0].text()).toEqual('Sales'); + expect(headerRows[1].findAll('th')[1].text()).toEqual('Profits'); + + expect(headerRows[2].findAll('th')[0].text()).toEqual('Last Year'); + expect(headerRows[2].findAll('th')[1].text()).toEqual('This Year'); + expect(headerRows[2].findAll('th')[2].text()).toEqual('Last Year'); + expect(headerRows[2].findAll('th')[3].text()).toEqual('This Year'); + + const footerRows = wrapper.findAll('.p-datatable-tfoot > tr'); + + expect(footerRows.length).toEqual(1); + + expect(footerRows[0].findAll('td')[0].attributes().colspan).toEqual('3'); + expect(footerRows[0].findAll('td')[1].text()).toEqual('Last Year Total'); + expect(footerRows[0].findAll('td')[2].text()).toEqual('This Year Total'); + }); + + + // sorting + it('should single sort', async () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData + }, + slots: { + default: ` + + + ` + } + }); + + const sortableTH = wrapper.findAll('.p-sortable-column')[0]; + const firstCellText = wrapper.findAll('.p-datatable-tbody > tr')[0].findAll('td')[1].text(); + const headerClick = jest.spyOn(wrapper.vm, 'onColumnHeaderClick'); + + await sortableTH.trigger('click'); + + expect(headerClick).toHaveBeenCalled(); + expect(sortableTH.classes()).toContain('p-highlight'); + expect(wrapper.findAll('.p-datatable-tbody > tr')[0].findAll('td')[1].text()).not.toEqual(firstCellText); + expect(wrapper.emitted()['update:sortField'][0]).toEqual(['code']); + expect(wrapper.emitted()['value-change'][0]).not.toBeNull(); + }); + + it('should multiple sort', async () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData, + sortMode: 'multiple' + }, + slots: { + default: ` + + + ` + } + }); + + const sortableTHs = wrapper.findAll('.p-sortable-column'); + const firstCellText = wrapper.findAll('.p-datatable-tbody > tr')[0].findAll('td')[1].text(); + const headerClick = jest.spyOn(wrapper.vm, 'onColumnHeaderClick'); + + await sortableTHs[0].trigger('click'); + + expect(headerClick).toHaveBeenCalled(); + expect(wrapper.findAll('.p-datatable-tbody > tr')[0].findAll('td')[1].text()).not.toEqual(firstCellText); + + const seconCellText = wrapper.findAll('.p-datatable-tbody > tr')[1].findAll('td')[1].text(); + + await sortableTHs[1].trigger('click'); + + expect(headerClick).toHaveBeenCalled(); + expect(sortableTHs[1].classes()).toContain('p-highlight'); + expect(wrapper.emitted()['update:multiSortMeta'][0]).toEqual([[{ field: 'code', order: 1 }]]); + expect(wrapper.emitted()['update:multiSortMeta'][1]).toEqual([[{ field: 'name', order: 1 }]]); + expect(wrapper.emitted()['value-change'][0]).not.toEqual(wrapper.emitted()['value-change'][1]); + expect(wrapper.findAll('.p-datatable-tbody > tr')[1].findAll('td')[1].text()).not.toEqual(seconCellText); + }); + + it('should have presort', async () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData, + sortOrder: -1, + sortField: 'code' + }, + slots: { + default: ` + + + ` + } + }); + + const presortedTH = wrapper.findAll('.p-sortable-column')[0]; + + expect(wrapper.findAll('.p-datatable-tbody > tr')[0].findAll('td')[1].text()).not.toEqual('Game Controller'); + expect(presortedTH.classes()).toContain('p-highlight'); + }); + + it('should remove sort', async () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData, + removableSort: true + }, + slots: { + default: ` + + + ` + } + }); + + const sortableTH = wrapper.findAll('.p-sortable-column')[0]; + const firstCellText = wrapper.findAll('.p-datatable-tbody > tr')[0].findAll('td')[1].text(); + + await sortableTH.trigger('click'); + + expect(wrapper.findAll('.p-datatable-tbody > tr')[0].findAll('td')[1].text()).not.toEqual(firstCellText); + expect(sortableTH.attributes()['aria-sort']).toBe('ascending'); + + const updatedFirstCellText = wrapper.findAll('.p-datatable-tbody > tr')[0].findAll('td')[1].text(); + + await sortableTH.trigger('click'); + + expect(wrapper.findAll('.p-datatable-tbody > tr')[0].findAll('td')[1].text()).not.toEqual(updatedFirstCellText); + expect(sortableTH.attributes()['aria-sort']).toBe('descending'); + + const latestUpdatedFirstCellText = wrapper.findAll('.p-datatable-tbody > tr')[0].findAll('td')[1].text(); + + await sortableTH.trigger('click'); + + expect(wrapper.findAll('.p-datatable-tbody > tr')[0].findAll('td')[1].text()).not.toEqual(latestUpdatedFirstCellText); + expect(sortableTH.attributes()['aria-sort']).toBe('none'); + }); + + + // filtering + it('should filtered globally', async () => { + await wrapper.setProps({ filters: { 'global': {value: 'b', matchMode: FilterMatchMode.STARTS_WITH} }}); + + await wrapper.vm.filter(smallData); + + expect(wrapper.emitted().filter[0][0].filteredValue.length).not.toEqual(3); + }); + + it('should filtered with menu display', async () => { + await wrapper.setProps({ filters: { 'name': {value: 'b', matchMode: FilterMatchMode.STARTS_WITH} }, + filterDisplay: 'menu', + globalFilterFields: ['name'] + }); + + await wrapper.vm.filter(smallData); + + expect(wrapper.emitted()['filter'][0][0].filteredValue.length).not.toEqual(3); + expect(wrapper.emitted()['value-change'][0][0].length).not.toEqual(3); + }); + + it('should filtered with row display', async () => { + await wrapper.setProps({ filters: { 'name': {value: 'b', matchMode: FilterMatchMode.STARTS_WITH} }, + filterDisplay: 'row', + globalFilterFields: ['name'] + }); + + await wrapper.vm.filter(smallData); + + expect(wrapper.emitted()['filter'][0][0].filteredValue.length).not.toEqual(3); + expect(wrapper.emitted()['value-change'][0][0].length).not.toEqual(3); + }); + + // selection + it('should single select', async () => { + await wrapper.setProps({ selection: null, selectionMode: 'single'}); + + await wrapper.vm.onRowClick({ + originalEvent: {target: wrapper.findAll('tr.p-selectable-row')[0]}, + data: smallData[0], + index: 0 + }); + + expect(wrapper.emitted()['row-click'][0][0].index).toEqual(0); + expect(wrapper.emitted()['update:selection'][0][0]).toEqual(smallData[0]); + expect(wrapper.emitted()['row-select'][0][0].index).toEqual(0); + }); + + it('should multiple selection with meta key', async () => { + await wrapper.setProps({ selection: null, selectionMode: 'multiple'}); + + await wrapper.vm.onRowClick({ + originalEvent: {shiftKey: true, target: wrapper.findAll('tr.p-selectable-row')[0]}, + data: smallData[0], + index: 0 + }); + + await wrapper.vm.onRowClick({ + originalEvent: {shiftKey: true, target: wrapper.findAll('tr.p-selectable-row')[1]}, + data: smallData[1], + index: 1 + }); + + expect(wrapper.emitted()['row-click'][0][0].index).toEqual(0); + expect(wrapper.emitted()['row-click'][1][0].index).toEqual(1); + expect(wrapper.emitted()['update:selection'][1][0]).toEqual([smallData[0], smallData[1]]); + }); + + it('should multiple selection without meta key', async () => { + await wrapper.setProps({ selection: null, selectionMode: 'multiple', metaKeySelection: false}); + + await wrapper.vm.onRowClick({ + originalEvent: {target: wrapper.findAll('tr.p-selectable-row')[0]}, + data: smallData[0], + index: 0 + }); + + await wrapper.vm.onRowClick({ + originalEvent: {target: wrapper.findAll('tr.p-selectable-row')[1]}, + data: smallData[1], + index: 1 + }); + + expect(wrapper.emitted()['row-click'][0][0].index).toEqual(0); + expect(wrapper.emitted()['row-click'][1][0].index).toEqual(1); + expect(wrapper.emitted()['update:selection'][0][0]).toEqual([smallData[0]]); + expect(wrapper.emitted()['update:selection'][1][0]).toEqual([smallData[1]]); + expect(wrapper.emitted()['row-select'][0][0].index).toBe(0); + expect(wrapper.emitted()['row-select'][1][0].index).toBe(1); + }); + + // radio selection + it('should select when radiobutton selection is enabled', async () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData, + selection: null + }, + slots: { + default: ` + + + + ` + } + }); + + expect(wrapper.findAll('td.p-selection-column').length).toBe(3); + expect(wrapper.findAll('.p-radiobutton').length).toBe(3); + + await wrapper.vm.toggleRowWithRadio({originalEvent: {}, data: smallData[0], index: 0}); + + expect(wrapper.emitted()['update:selection'][0][0]).toEqual(smallData[0]); + expect(wrapper.emitted()['row-select'][0][0].index).toBe(0); + + await wrapper.vm.toggleRowWithRadio({originalEvent: {}, data: smallData[1], index: 1}); + + expect(wrapper.emitted()['update:selection'][1][0]).toEqual(smallData[1]); + expect(wrapper.emitted()['row-select'][1][0].index).toBe(1); + }); + + // checkbox selection + it('should select when checkbox selection is enabled', async () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData, + selection: null + }, + slots: { + default: ` + + + + ` + } + }); + + expect(wrapper.findAll('.p-checkbox').length).toBe(4); + + await wrapper.vm.toggleRowWithCheckbox({originalEvent: {}, data: smallData[0], index: 0}); + + expect(wrapper.emitted()['update:selection'][0][0]).toEqual([smallData[0]]); + expect(wrapper.emitted()['row-select'][0][0].index).toBe(0); + + await wrapper.vm.toggleRowWithCheckbox({originalEvent: {}, data: smallData[1], index: 1}); + + expect(wrapper.emitted()['update:selection'][1][0]).toEqual([smallData[1]]); + expect(wrapper.emitted()['row-select'][1][0].index).toBe(1); + }); + + it('should select all rows', async () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData, + selection: null + }, + slots: { + default: ` + + + + ` + } + }); + + await wrapper.vm.toggleRowsWithCheckbox({originalEvent: {}, checked: true}); + + expect(wrapper.emitted()['row-select-all'][0][0].data).toEqual(smallData); + expect(wrapper.emitted()['update:selection'][0][0]).toEqual(smallData); + }); + + it('should unselect all rows', async () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData, + selection: smallData + }, + slots: { + default: ` + + + + ` + } + }); + + await wrapper.vm.toggleRowsWithCheckbox({originalEvent: {}, checked: false}); + + expect(wrapper.emitted()['row-unselect-all'][0][0].data).toBe(undefined); + expect(wrapper.emitted()['update:selection'][0][0]).toEqual([]); + }); + + + // scrolling + it('should scrolling', async () => { + await wrapper.setProps({scrollable: true}); + + expect(wrapper.find('.p-datatable-scrollable').exists()).toBe(true); + }); + + it('should vertical scroll', async () => { + await wrapper.setProps({scrollable: true, scrollHeight: '100px'}); + + expect(wrapper.find('.p-datatable-wrapper').attributes().style).toBe('max-height: 100px;'); + }); + + it('should flex scrolling', async () => { + await wrapper.setProps({scrollable: true, scrollHeight: 'flex'}); + + expect(wrapper.find('.p-datatable-flex-scrollable').exists()).toBe(true); + }); + + it('should both scrolling', async () => { + await wrapper.setProps({scrollable: true, scrollHeight: '100px', scrollDirection: 'both'}); + + expect(wrapper.find('.p-datatable-scrollable-both').exists()).toBe(true); + }); + + it('should have frozen rows', async () => { + await wrapper.setProps({frozenValue: [smallData[0]], scrollable: true, scrollHeight: '100px', scrollDirection: 'both'}); + + expect(wrapper.findAll('.p-datatable-tbody')[0].classes()).toContain('p-datatable-frozen-tbody'); + expect(wrapper.findAll('.p-datatable-tbody')[0].attributes().style).toBe('top: 0px;'); + }); + + // frozen columns + it('should have frozen columns', () => { + wrapper = null; + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData, + selection: null, + scrollable: true, + scrollDirection: 'both' + }, + slots: { + default: ` + + + ` + } + }); + + expect(wrapper.find('th.p-frozen-column').exists()).toBe(true); + // expect(wrapper.find('th.p-frozen-column').attributes().style).toBe('left: 0px;'); + expect(wrapper.findAll('td.p-frozen-column').length).toBe(3); + // expect(wrapper.findAll('td.p-frozen-column')[0].attributes().style).toBe('left: 0px;'); + }); + + + // lazy loading + + + // row expansion + it('should have row toggler', () => { + expect(wrapper.findAll('.p-row-toggler').length).toBe(3); + }); + + it('should expand a row', async () => { + await wrapper.setProps({ expandedRows: [] }); + + await wrapper.vm.toggleRow({ originalEvent: {}, data: smallData[0]}); + + expect(wrapper.emitted()['update:expandedRows'][0][0]).toEqual([smallData[0]]); + expect(wrapper.emitted()['row-expand'][0][0].data).toEqual(smallData[0]); + }); + + it('should collapse a row', async () => { + await wrapper.setProps({ expandedRows: [smallData[0]] }); + + await wrapper.vm.toggleRow({ originalEvent: {}, data: smallData[0]}); + + expect(wrapper.emitted()['update:expandedRows'][0][0]).toEqual([]); + expect(wrapper.emitted()['row-collapse'][0][0].data).toEqual(smallData[0]); + }); + + + // editing + // cell editing + + // row editing + it('should init row editing', async () => { + wrapper = mount(DataTable, { + global: { + components: { + Column, + InputText + } + }, + props: { + value: smallData, + editMode: 'row', + editingRows: [] + }, + slots: { + default: ` + + + + + + + + ` + } + }); + + expect(wrapper.findAll('.p-editable-column').length).toBe(6); + expect(wrapper.findAll('.p-row-editor-init').length).toBe(3); + + await wrapper.vm.onRowEditInit({ data: smallData[0] }); + + expect(wrapper.emitted()['update:editingRows'][0][0]).toEqual([smallData[0]]); + expect(wrapper.emitted()['row-edit-init'][0][0].data).toEqual(smallData[0]); + expect(wrapper.findAll('.p-datatable-tbody > tr > td')[wrapper.findAll('.p-datatable-tbody > tr > td').length - 1].find('.p-row-editor-init').exists()).toBe(true); + }); + + it('should save row editing', async () => { + wrapper = mount(DataTable, { + global: { + components: { + Column, + InputText + } + }, + props: { + value: smallData, + editMode: 'row', + editingRows: [] + }, + slots: { + default: ` + + + + + + + + ` + } + }); + + await wrapper.vm.onRowEditSave({data: { "id": "9999", "code": "vbb124btr", "name": "Game Controller"}}); + + expect(wrapper.emitted()['update:editingRows'][0][0]).toEqual([]); + expect(wrapper.emitted()['row-edit-save'][0][0].data).toEqual({ "id": "9999", "code": "vbb124btr", "name": "Game Controller"}); + }); + + it('should cancel row editing', async () => { + wrapper = mount(DataTable, { + global: { + components: { + Column, + InputText + } + }, + props: { + value: smallData, + editMode: 'row', + editingRows: [] + }, + slots: { + default: ` + + + + + + + + ` + } + }); + + await wrapper.vm.onRowEditCancel({ data: smallData[0] }); + + expect(wrapper.emitted()['update:editingRows'][0][0]).toEqual([]); + expect(wrapper.emitted()['row-edit-cancel'][0][0].data).toEqual(smallData[0]); + }); + + + // column resize + it('should fit mode expanding exists', () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData, + resizableColumns: true, + columnResizeMode: 'fit' + }, + slots: { + default: ` + + + ` + } + }); + + expect(wrapper.find('.p-datatable.p-component').classes()).toContain('p-datatable-resizable'); + expect(wrapper.find('.p-datatable.p-component').classes()).toContain('p-datatable-resizable-fit'); + expect(wrapper.findAll('.p-column-resizer').length).toBe(2); + }); + + it('should fit mode resize start', async () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData, + resizableColumns: true, + columnResizeMode: 'fit' + }, + slots: { + default: ` + + + ` + } + }); + + const resizer = wrapper.findAll('.p-column-resizer')[0]; + + await wrapper.vm.onColumnResizeStart({target: resizer.element}); + + expect(wrapper.componentVM.columnResizing).toBe(true); + expect(wrapper.find('.p-column-resizer-helper').attributes().style).toContain('display: none;'); + }); + + it('should fit mode resize', async () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData, + resizableColumns: true, + columnResizeMode: 'fit' + }, + slots: { + default: ` + + + ` + } + }); + + await wrapper.vm.onColumnResize({}); + + expect(wrapper.find('.p-column-resizer-helper').attributes().style).toContain('display: block;'); + }); + + it('should fit mode column resize end', async () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData, + resizableColumns: true, + columnResizeMode: 'fit' + }, + slots: { + default: ` + + + ` + } + }); + + const resizer = wrapper.findAll('.p-column-resizer')[0]; + + await wrapper.vm.onColumnResizeStart({target: resizer.element}); + + await wrapper.vm.onColumnResizeEnd(); + + expect(wrapper.find('.p-column-resizer-helper').attributes().style).toContain('display: none;'); + }); + + it('should expand mode resize start', async () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData, + resizableColumns: true, + columnResizeMode: 'expand' + }, + slots: { + default: ` + + + ` + } + }); + + const resizer = wrapper.findAll('.p-column-resizer')[0]; + + await wrapper.vm.onColumnResizeStart({target: resizer.element}); + + expect(wrapper.componentVM.columnResizing).toBe(true); + expect(wrapper.find('.p-column-resizer-helper').attributes().style).toContain('display: none;'); + }); + + it('should fit mode resize', async () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData, + resizableColumns: true, + columnResizeMode: 'expand' + }, + slots: { + default: ` + + + ` + } + }); + + await wrapper.vm.onColumnResize({}); + + expect(wrapper.find('.p-column-resizer-helper').attributes().style).toContain('display: block;'); + }); + + it('should fit mode column resize end', async () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData, + resizableColumns: true, + columnResizeMode: 'expand' + }, + slots: { + default: ` + + + ` + } + }); + + const resizer = wrapper.findAll('.p-column-resizer')[0]; + + await wrapper.vm.onColumnResizeStart({target: resizer.element}); + + await wrapper.vm.onColumnResizeEnd(); + + expect(wrapper.find('.p-column-resizer-helper').attributes().style).toContain('display: none;'); + }); + + + // column reorder + it('should reorder columns', async () => { + await wrapper.setProps({ reorderableColumns: true }); + + expect(wrapper.find('.p-datatable-reorder-indicator-up').exists()).toBe(true); + expect(wrapper.find('.p-datatable-reorder-indicator-down').exists()).toBe(true); + }); + + + // row reorder + it('should exist', () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData + }, + slots: { + default: ` + + + + ` + } + }); + + expect(wrapper.findAll('.p-datatable-reorderablerow-handle').length).toBe(3); + }); + + + // row group + // subheader grouping + it('should exist', () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData, + rowGroupMode: 'subheader', + groupRowsBy: 'name', + sortMode: 'single', + sortField: 'name', + sortOrder: 1, + scrollable: true + }, + slots: { + default: ` + + + `, + groupheader: `GroupHeader Templating`, + groupfooter: `GroupFooter Templating` + } + }); + + expect(wrapper.find('.p-datatable-tbody').attributes().role).toBe('rowgroup'); + expect(wrapper.findAll('.p-column-header-content').length).toBe(1); + expect(wrapper.find('.p-column-header-content').text()).toBe('Code'); + }); + + it('should have groupheader templating', () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData, + rowGroupMode: 'subheader', + groupRowsBy: 'name', + sortMode: 'single', + sortField: 'name', + sortOrder: 1, + scrollable: true + }, + slots: { + default: ` + + + `, + groupheader: `GroupHeader Templating`, + groupfooter: `GroupFooter Templating` + } + }); + + expect(wrapper.findAll('.p-rowgroup-header').length).toBe(3); + expect(wrapper.find('.p-rowgroup-header').text()).toBe('GroupHeader Templating'); + }); + + it('should have groupfooter templating', () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: smallData, + rowGroupMode: 'subheader', + groupRowsBy: 'name', + sortMode: 'single', + sortField: 'name', + sortOrder: 1, + scrollable: true + }, + slots: { + default: ` + + + `, + groupheader: `GroupHeader Templating`, + groupfooter: `GroupFooter Templating` + } + }); + + expect(wrapper.findAll('.p-rowgroup-header').length).toBe(3); + expect(wrapper.find('.p-rowgroup-footer').text()).toBe('GroupFooter Templating'); + }); + + it('should have expandable row groups and expand rows', async () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: [ + { + "id":1000, + "name":"James Butt", + "country":{ + "name":"Algeria", + "code":"dz" + }, + "company":"Benton, John B Jr", + "representative":{ + "name":"Ioni Bowcher", + "image":"ionibowcher.png" + } + }, + { + "id":1001, + "name":"Josephine Darakjy", + "country":{ + "name":"Egypt", + "code":"eg" + }, + "company":"Chanay, Jeffrey A Esq", + "representative":{ + "name":"Amy Elsner", + "image":"amyelsner.png" + } + }, + { + "id":1013, + "name":"Graciela Ruta", + "country":{ + "name":"Chile", + "code":"cl" + }, + "company":"Buckley Miller & Wright", + "representative":{ + "name":"Amy Elsner", + "image":"amyelsner.png" + } + }, + { + "id":1021, + "name":"Veronika Inouye", + "country":{ + "name":"Ecuador", + "code":"ec" + }, + "company":"C 4 Network Inc", + "representative":{ + "name":"Ioni Bowcher", + "image":"ionibowcher.png" + } + }, + { + "id":1026, + "name":"Chanel Caudy", + "country":{ + "name":"Argentina", + "code":"ar" + }, + "company":"Professional Image Inc", + "representative":{ + "name":"Ioni Bowcher", + "image":"ionibowcher.png" + } + }, + { + "id":1027, + "name":"Ezekiel Chui", + "country":{ + "name":"Ireland", + "code":"ie" + }, + "company":"Sider, Donald C Esq", + "representative":{ + "name":"Amy Elsner", + "image":"amyelsner.png" + } + } + ], + rowGroupMode: 'subheader', + groupRowsBy: 'representative.name', + sortMode: 'single', + sortField: 'representative.name', + sortOrder: 1, + scrollable: true, + expandableRowGroups: true, + expandedRowGroups: null, + responsiveLayout: 'scroll' + }, + slots: { + default: ` + + + + + `, + groupheader: ` + + ` + } + }); + + expect(wrapper.findAll('.p-datatable-thead > tr > th').length).toBe(3); + expect(wrapper.findAll('.p-datatable-tbody > tr.p-rowgroup-header').length).toBe(2); + expect(wrapper.findAll('.p-datatable-tbody > tr.p-rowgroup-header')[0].text()).toBe('Amy Elsner'); + + const firstToggler = wrapper.findAll('.p-row-toggler')[0]; + + await firstToggler.trigger('click'); + + expect(wrapper.emitted()['update:expandedRowGroups'][0]).toEqual([['Amy Elsner']]); + expect(wrapper.emitted()['rowgroup-expand'][0][0].data).toBe('Amy Elsner'); + }); + + it('should have rowspan grouping', async () => { + wrapper = mount(DataTable, { + global: { + components: { + Column + } + }, + props: { + value: [ + { + "id": "1000", + "code": "vbb124btr", + "name": "Game Controller" + }, + { + "id": "1001", + "code": "nvklal433", + "name": "Black Watch" + }, + { + "id": "1002", + "code": "zz21cz3c1", + "name": "Blue Band" + }, + { + "id": "1003", + "code": "vbb124btrvbb124btr", + "name": "Game Controller" + }, + { + "id": "1004", + "code": "nvklal433nvklal433", + "name": "Black Watch" + }, + { + "id": "1006", + "code": "zz21cz3c1zz21cz3c1", + "name": "Blue Band" + } + ], + rowGroupMode: 'rowspan', + groupRowsBy: 'name', + sortMode: 'single', + sortField: 'name', + sortOrder: 1, + scrollable: true, + responsiveLayout: 'scroll' + }, + slots: { + default: ` + + + + + + ` + } + }); + + expect(wrapper.find('.p-datatable-thead > tr').findAll('th')[0].text()).toBe('#'); + expect(wrapper.findAll('.p-datatable-tbody > tr')[0].findAll('td')[0].text()).toBe('0'); + expect(wrapper.findAll('.p-datatable-tbody > tr')[0].findAll('td')[1].text()).toBe('Black Watch'); + expect(wrapper.findAll('.p-datatable-tbody > tr')[0].findAll('td')[1].attributes().rowspan).toBe('2'); + expect(wrapper.findAll('.p-datatable-tbody > tr')[2].findAll('td')[1].attributes().rowspan).toBe('2'); + expect(wrapper.findAll('.p-datatable-tbody > tr')[4].findAll('td')[1].attributes().rowspan).toBe('2'); + }); + + + // export + it('should export table', async () => { + const exportCSV = jest.spyOn(wrapper.vm, 'exportCSV'); + + await wrapper.vm.exportCSV(); + + expect(exportCSV).toHaveBeenCalled(); + }); + + + // state + it('should get storage', async () => { + await wrapper.setProps({ stateStorage: 'session', stateKey: 'dt-state-demo-session', paginator: true }); + + await wrapper.vm.getStorage(); + + expect(wrapper.emitted()['state-save'][0]).not.toBeNull(); + }); + + it('should save session storage', async () => { + jest.spyOn(window.sessionStorage.__proto__, 'setItem'); + window.sessionStorage.__proto__.setItem = jest.fn(); + + await wrapper.vm.saveState(); + + expect(sessionStorage.setItem).toHaveBeenCalled(); + expect(wrapper.emitted()['state-save'][0]).not.toBeNull(); + }); + + it('should save local storage', async () => { + jest.spyOn(window.localStorage.__proto__, 'setItem'); + window.localStorage.__proto__.setItem = jest.fn(); + + await wrapper.vm.saveState(); + + expect(localStorage.setItem).toHaveBeenCalled(); + expect(wrapper.emitted()['state-save'][0]).not.toBeNull(); + }); + + + // contextmenu + + + // responsive + it('should have stack layout', () => { + expect(wrapper.find('.p-datatable').classes()).toContain('p-datatable-responsive-stack'); + }); + + it('should have scroll layout', async () => { + await wrapper.setProps({ responsiveLayout: 'scroll' }); + + expect(wrapper.find('.p-datatable').classes()).toContain('p-datatable-responsive-scroll'); + }); + + + // row styling +}); \ No newline at end of file diff --git a/components/datatable/DataTable.vue b/components/datatable/DataTable.vue new file mode 100755 index 000000000..afe4b388f --- /dev/null +++ b/components/datatable/DataTable.vue @@ -0,0 +1,2259 @@ + + + + + diff --git a/components/datatable/FooterCell.vue b/components/datatable/FooterCell.vue new file mode 100644 index 000000000..614a7f7c9 --- /dev/null +++ b/components/datatable/FooterCell.vue @@ -0,0 +1,75 @@ + + + diff --git a/components/datatable/HeaderCell.vue b/components/datatable/HeaderCell.vue new file mode 100644 index 000000000..c963348e0 --- /dev/null +++ b/components/datatable/HeaderCell.vue @@ -0,0 +1,249 @@ + + + diff --git a/components/datatable/HeaderCheckbox.vue b/components/datatable/HeaderCheckbox.vue new file mode 100755 index 000000000..c867488ea --- /dev/null +++ b/components/datatable/HeaderCheckbox.vue @@ -0,0 +1,41 @@ + + + diff --git a/components/datatable/RowCheckbox.vue b/components/datatable/RowCheckbox.vue new file mode 100755 index 000000000..20929bbae --- /dev/null +++ b/components/datatable/RowCheckbox.vue @@ -0,0 +1,42 @@ + + + diff --git a/components/datatable/RowRadioButton.vue b/components/datatable/RowRadioButton.vue new file mode 100755 index 000000000..5980775a2 --- /dev/null +++ b/components/datatable/RowRadioButton.vue @@ -0,0 +1,42 @@ + + + diff --git a/components/datatable/TableBody.vue b/components/datatable/TableBody.vue new file mode 100755 index 000000000..178688603 --- /dev/null +++ b/components/datatable/TableBody.vue @@ -0,0 +1,526 @@ + + + diff --git a/components/datatable/TableFooter.vue b/components/datatable/TableFooter.vue new file mode 100755 index 000000000..d146558cb --- /dev/null +++ b/components/datatable/TableFooter.vue @@ -0,0 +1,93 @@ + + + diff --git a/components/datatable/TableHeader.vue b/components/datatable/TableHeader.vue new file mode 100755 index 000000000..b50e0289f --- /dev/null +++ b/components/datatable/TableHeader.vue @@ -0,0 +1,177 @@ + + + diff --git a/components/datatable/TableLoadingBody.vue b/components/datatable/TableLoadingBody.vue new file mode 100755 index 000000000..f1c25045c --- /dev/null +++ b/components/datatable/TableLoadingBody.vue @@ -0,0 +1,25 @@ + + + diff --git a/components/datatable/package.json b/components/datatable/package.json new file mode 100644 index 000000000..ad93005e2 --- /dev/null +++ b/components/datatable/package.json @@ -0,0 +1,9 @@ +{ + "main": "./datatable.cjs.js", + "module": "./datatable.esm.js", + "unpkg": "./datatable.min.js", + "types": "./DataTable.d.ts", + "browser": { + "./sfc": "./DataTable.vue" + } +} \ No newline at end of file diff --git a/components/dataview/DataView.d.ts b/components/dataview/DataView.d.ts new file mode 100755 index 000000000..4e60fd555 --- /dev/null +++ b/components/dataview/DataView.d.ts @@ -0,0 +1,212 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type DataViewLayoutType = 'list' | 'grid' | undefined; + +type DataTablePositionType = 'top' | 'bottom' | 'both' | undefined; + +type DataViewSortFieldType = string | ((item: any) => string) | undefined; + +export interface DataViewPageEvent { + /** + * New page number + */ + page: number; + /** + * Index of first record + */ + first: number; + /** + * Number of rows to display in new page + */ + rows: number; + /** + * Total number of pages + */ + pageCount: number; +} + +export interface DataViewProps { + /** + * An array of objects to display. + */ + value?: any[] | undefined; + /** + * Layout of the items, valid values are 'list' and 'grid'. + * @see DataViewLayoutType + * Default value is 'list'. + */ + layout?: any; + /** + * Number of rows to display per page. + * Default value is 0. + */ + rows?: number | undefined; + /** + * Index of the first record to render. + * Default value is 0. + */ + first?: number | undefined; + /** + * Number of total records, defaults to length of value when not defined. + */ + totalRecords?: number | undefined; + /** + * When specified as true, enables the pagination. + */ + paginator?: boolean | undefined; + /** + * Position of the paginator, options are 'top','bottom' or 'both'. + * @see DataTablePositionType + * Default value is 'bottom'. + */ + paginatorPosition?: DataTablePositionType; + /** + * Whether to show it even there is only one page. + * Default value is true. + */ + alwaysShowPaginator?: boolean | undefined; + /** + * Template of the paginator. It can be customized using the template property using the predefined keys, default value is 'FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown'. Here are the available elements that can be placed inside a paginator in any order. + * + * - FirstPageLink + * - PrevPageLink + * - PageLinks + * - NextPageLink + * - LastPageLink + * - RowsPerPageDropdown + * - JumpToPageDropdown + * - JumpToPageInput + * - CurrentPageReport + */ + paginatorTemplate?: string | undefined; + /** + * Number of page links to display. + * Default value is 5. + */ + pageLinkSize?: number | undefined; + /** + * Array of integer values to display inside rows per page dropdown. + */ + rowsPerPageOptions?: number[] | undefined; + /** + * Template of the current page report element. It displays information about the pagination state. Default value is ({currentPage} of {totalPages}) whereas available placeholders are the following; + * + * - {currentPage} + * - {totalPages} + * - {rows} + * - {first} + * - {last} + * - {totalRecords} + */ + currentPageReportTemplate?: string | undefined; + /** + * Property name or a getter function of data to use in sorting by default. + * @see DataViewSortFieldType + */ + sortField?: DataViewSortFieldType; + /** + * Order to sort the data by default. + */ + sortOrder?: number | undefined; + /** + * Defines if data is loaded and interacted with in lazy manner. + */ + lazy?: boolean | undefined; + /** + * Name of the data that uniquely identifies the a record in the data. + */ + dataKey: string | undefined; +} + +export interface DataViewSlots { + /** + * Custom header template. + */ + header: () => VNode[]; + /** + * Custom footer template. + */ + footer: () => VNode[]; + /** + * Custom empty template. + */ + empty: () => VNode[]; + /** + * Custom paginator start template. + */ + paginatorstart: () => VNode[]; + /** + * Custom paginator end template. + */ + paginatorend: () => VNode[]; + /** + * Custom list template. + * @param {Object} scope - list slot's params. + */ + list: (scope: { + /** + * Value of the component + */ + data: any; + /** + * Index of the grid + */ + index: number; + }) => VNode[]; + /** + * Custom list template. + * @param {Object} scope - list slot's params. + */ + grid: (scope: { + /** + * Value of the component + */ + data: any; + /** + * Index of the grid + */ + index: number; + }) => VNode[]; +} + +export declare type DataViewEmits = { + /** + * Emitted when the first changes. + * @param {number} value - New value. + */ + 'update:first': (value: number) => void; + /** + * Emitted when the rows changes. + * @param {number} value - New value. + */ + 'update:rows': (value: number) => void; + /** + * Callback to invoke when page changes, the event object contains information about the new state. + * @param {DataViewPageEvent} event - Custom page event. + */ + 'page': (event: DataViewPageEvent) => void; +} + +declare class DataView extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + DataView: GlobalComponentConstructor + } +} + +/** + * + * DataView displays data in grid or list layout with pagination and sorting features. + * + * Helper API: + * + * - PrimeFlex + * + * Demos: + * + * - [DataView](https://www.primefaces.org/primevue/showcase/#/dataview) + * + */ +export default DataView; diff --git a/components/dataview/DataView.spec.js b/components/dataview/DataView.spec.js new file mode 100644 index 000000000..3f32de26f --- /dev/null +++ b/components/dataview/DataView.spec.js @@ -0,0 +1,82 @@ +import { mount } from '@vue/test-utils'; +import DataView from './DataView.vue'; + +describe('DataView.vue', () => { + it('should exist', () => { + const wrapper = mount(DataView, { + props: { + value: [ + { + "id": "1000", + "code": "f230fh0g3", + "name": "Bamboo Watch", + "description": "Product Description", + "image": "bamboo-watch.jpg", + "price": 65, + "category": "Accessories", + "quantity": 24, + "inventoryStatus": "INSTOCK", + "rating": 5 + }, + { + "id": "1001", + "code": "nvklal433", + "name": "Black Watch", + "description": "Product Description", + "image": "black-watch.jpg", + "price": 72, + "category": "Accessories", + "quantity": 61, + "inventoryStatus": "INSTOCK", + "rating": 4 + }, + { + "id": "1002", + "code": "zz21cz3c1", + "name": "Blue Band", + "description": "Product Description", + "image": "blue-band.jpg", + "price": 79, + "category": "Fitness", + "quantity": 2, + "inventoryStatus": "LOWSTOCK", + "rating": 3 + } + ], + layout: 'grid', + paginator: true, + rows: 3 + }, + slots: { + grid: ` + + ` + } + }); + + expect(wrapper.find('.p-dataview.p-component').exists()).toBe(true); + expect(wrapper.find('.p-dataview-grid').exists()).toBe(true); + expect(wrapper.findAll('.product-grid-item').length).toBe(3); + expect(wrapper.find('.p-paginator.p-component').exists()).toBe(true); + }); +}); \ No newline at end of file diff --git a/components/dataview/DataView.vue b/components/dataview/DataView.vue new file mode 100755 index 000000000..73f47a37f --- /dev/null +++ b/components/dataview/DataView.vue @@ -0,0 +1,230 @@ + + diff --git a/components/dataview/package.json b/components/dataview/package.json new file mode 100644 index 000000000..edf9eef65 --- /dev/null +++ b/components/dataview/package.json @@ -0,0 +1,9 @@ +{ + "main": "./dataview.cjs.js", + "module": "./dataview.esm.js", + "unpkg": "./dataview.min.js", + "types": "./DataView.d.ts", + "browser": { + "./sfc": "./DataView.vue" + } +} \ No newline at end of file diff --git a/components/dataviewlayoutoptions/DataViewLayoutOptions.d.ts b/components/dataviewlayoutoptions/DataViewLayoutOptions.d.ts new file mode 100755 index 000000000..7a34110e0 --- /dev/null +++ b/components/dataviewlayoutoptions/DataViewLayoutOptions.d.ts @@ -0,0 +1,39 @@ +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface DataViewLayoutOptionsProps { + /** + * Value of the component. + */ + modelValue?: string | undefined; +} + +export interface DataViewLayoutOptionsSlots { +} + +export declare type DataViewLayoutOptionsEmits = { + /** + * Emitted when the value changes. + * @param {*} value - New value. + */ + 'update:modelValue': (value: string) => void; +} + +declare class DataViewLayoutOptions extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + DataViewLayoutOptions: GlobalComponentConstructor + } +} + +/** + * + * When both layout modes are enabled in DataView, a UI element would be necessary to let the user toggle between the view. + * DataViewLayoutOptions is a helper component to display a buttonset to choose the layout mode in DataView. + * + * Demos: + * + * - [DataViewLayoutOptions](https://www.primefaces.org/primevue/showcase/#/dataview) + * + */ +export default DataViewLayoutOptions; diff --git a/components/dataviewlayoutoptions/DataViewLayoutOptions.spec.js b/components/dataviewlayoutoptions/DataViewLayoutOptions.spec.js new file mode 100644 index 000000000..28019c8ec --- /dev/null +++ b/components/dataviewlayoutoptions/DataViewLayoutOptions.spec.js @@ -0,0 +1,19 @@ +import { mount } from '@vue/test-utils'; +import DataViewLayoutOptions from './DataViewLayoutOptions.vue'; + +describe('DataViewLayoutOptions.vue', () => { + it('should exist', async () => { + const wrapper = mount(DataViewLayoutOptions, { + props: { + modelValue: 'grid' + } + }); + + expect(wrapper.find('.p-dataview-layout-options').exists()).toBe(true); + expect(wrapper.find('.p-highlight > .pi-th-large').exists()).toBe(true); + + wrapper.vm.$emit('update:modelValue', 'list'); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual(['list']); + }); +}); \ No newline at end of file diff --git a/components/dataviewlayoutoptions/DataViewLayoutOptions.vue b/components/dataviewlayoutoptions/DataViewLayoutOptions.vue new file mode 100755 index 000000000..e922a4426 --- /dev/null +++ b/components/dataviewlayoutoptions/DataViewLayoutOptions.vue @@ -0,0 +1,39 @@ + + + diff --git a/components/dataviewlayoutoptions/package.json b/components/dataviewlayoutoptions/package.json new file mode 100644 index 000000000..d6e721909 --- /dev/null +++ b/components/dataviewlayoutoptions/package.json @@ -0,0 +1,9 @@ +{ + "main": "./dataviewlayoutoptions.cjs.js", + "module": "./dataviewlayoutoptions.esm.js", + "unpkg": "./dataviewlayoutoptions.min.js", + "types": "./DataViewLayoutOptions.d.ts", + "browser": { + "./sfc": "./DataViewLayoutOptions.vue" + } +} \ No newline at end of file diff --git a/components/deferredcontent/DeferredContent.d.ts b/components/deferredcontent/DeferredContent.d.ts new file mode 100755 index 000000000..a92ab34fa --- /dev/null +++ b/components/deferredcontent/DeferredContent.d.ts @@ -0,0 +1,38 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface DeferredContentProps { +} + +export interface DeferredContentSlots { + /** + * Default content slot. + */ + default: () => VNode[]; +} + +export declare type DeferredContentEmits = { + /** + * Callback to invoke when deferred content is loaded. + */ + 'load': () => void; +} + +declare class DeferredContent extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + DeferredContent: GlobalComponentConstructor + } +} + +/** + * + * DeferredContent postpones the loading the content that is initially not in the viewport until it becomes visible on scroll. + * + * Demos: + * + * - [DeferredContent](https://www.primefaces.org/primevue/showcase/#/deferredcontent) + * + */ +export default DeferredContent; diff --git a/components/deferredcontent/DeferredContent.spec.js b/components/deferredcontent/DeferredContent.spec.js new file mode 100644 index 000000000..058256f33 --- /dev/null +++ b/components/deferredcontent/DeferredContent.spec.js @@ -0,0 +1,15 @@ +import { mount } from '@vue/test-utils'; +import DeferredContent from './DeferredContent.vue'; + +describe('DeferredContent', () => { + it('should exist', async () => { + const wrapper = mount(DeferredContent, { + slots: { + default: 'Nature' + } + }); + + await wrapper.setData({ loaded: true }); + expect(wrapper.find('img').exists()).toBe(true); + }); +}); \ No newline at end of file diff --git a/components/deferredcontent/DeferredContent.vue b/components/deferredcontent/DeferredContent.vue new file mode 100755 index 000000000..c238723a6 --- /dev/null +++ b/components/deferredcontent/DeferredContent.vue @@ -0,0 +1,62 @@ + + + diff --git a/components/deferredcontent/package.json b/components/deferredcontent/package.json new file mode 100644 index 000000000..0b25a42fb --- /dev/null +++ b/components/deferredcontent/package.json @@ -0,0 +1,9 @@ +{ + "main": "./deferredcontent.cjs.js", + "module": "./deferredcontent.esm.js", + "unpkg": "./deferredcontent.min.js", + "types": "./DeferredContent.d.ts", + "browser": { + "./sfc": "./DeferredContent.vue" + } +} \ No newline at end of file diff --git a/components/dialog/Dialog.d.ts b/components/dialog/Dialog.d.ts new file mode 100755 index 000000000..90ce69f66 --- /dev/null +++ b/components/dialog/Dialog.d.ts @@ -0,0 +1,204 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type DialogPositionType = 'center' | 'top' | 'bottom' | 'left' | 'right' | 'topleft' | 'topright' | 'bottomleft' | 'bottomright' | undefined; + +type DialogAppendToType = 'body' | 'self' | string | undefined | HTMLElement; + +export interface DialogBreakpoints { + /** + * Breakpoint for responsive mode. + * + * Example: + * + * + * + * Result: + * + * @media screen and (max-width: ${breakpoint[key]}) { + * .p-dialog[attributeSelector] { + * width: ${breakpoint[value]} !important; + * } + * } + */ + [key: string]: string; +} + +export interface DialogProps { + /** + * Title content of the dialog. + */ + header?: string | undefined; + /** + * Footer content of the dialog. + */ + footer?: string | undefined; + /** + * Specifies the visibility of the dialog. + */ + visible?: boolean | undefined; + /** + * Defines if background should be blocked when dialog is displayed. + */ + modal?: boolean | undefined; + /** + * Style of the content section. + */ + contentStyle?: any; + /** + * Style class of the content section. + */ + contentClass?: any; + /** + * When enabled dialog is displayed in RTL direction. + */ + rtl?: boolean | undefined; + /** + * Adds a close icon to the header to hide the dialog. + * Default value is true. + */ + closable?: boolean | undefined; + /** + * Specifies if clicking the modal background should hide the dialog. + */ + dismissableMask?: boolean | undefined; + /** + * Specifies if pressing escape key should hide the dialog. + * Default value is true. + */ + closeOnEscape?: boolean | undefined; + /** + * Whether to show the header or not. + * Default value is true. + */ + showHeader?: boolean | undefined; + /** + * Base zIndex value to use in layering. + * Default value is 0. + */ + baseZIndex?: number | undefined; + /** + * Whether to automatically manage layering. + * Default value is true. + */ + autoZIndex?: boolean | undefined; + /** + * Aria label of the close icon. + * Default value is 'close'. + */ + ariaCloseLabel?: string | undefined; + /** + * Position of the dialog, options are 'center', 'top', 'bottom', 'left', 'right', 'topleft', 'topright', 'bottomleft' or 'bottomright'. + * @see DialogPositionType + * Default value is 'center'. + */ + position?: DialogPositionType; + /** + * Whether the dialog can be displayed full screen. + */ + maximizable?: boolean | undefined; + /** + * Object literal to define widths per screen size. + * @see DialogBreakpoints + */ + breakpoints?: DialogBreakpoints; + /** + * Enables dragging to change the position using header. + * Default value is true. + */ + draggable?: boolean | undefined; + /** + * Keeps dialog in the viewport when dragging. + * Default value is true. + */ + keepInViewPort?: boolean | undefined; + /** + * Minimum value for the left coordinate of dialog in dragging. + * Default value is 0. + */ + minX?: number | undefined; + /** + * Minimum value for the top coordinate of dialog in dragging. + * Default value is 0. + */ + minY?: number | undefined; + /** + * A valid query selector or an HTMLElement to specify where the dialog gets attached. Special keywords are 'body' for document body and 'self' for the element itself. + * @see DialogAppendToType + * Default value is 'body'. + */ + appendTo?: DialogAppendToType; + /** + * Style of the dynamic dialog. + */ + style?: any; +} + +export interface DialogSlots { + /** + * Default content slot. + */ + default: () => VNode[]; + /** + * Custom header template. + */ + header: () => VNode[]; + /** + * Custom footer template. + */ + footer: () => VNode[]; +} + +export declare type DialogEmits = { + /** + * Emitted when the visible changes. + * @param {boolean} value - New value. + */ + 'update:visible': (value: boolean) => void; + /** + * Callback to invoke when dialog is hidden. + */ + 'hide': () => void; + /** + * Callback to invoke after dialog is hidden. + */ + 'after-hide': () => void; + /** + * Callback to invoke when dialog is shown. + */ + 'show': () => void; + /** + * Fired when a dialog gets maximized. + * @param {event} event - Browser event. + */ + 'maximize': (event: Event) => void; + /** + * Fired when a dialog gets unmaximized. + * @param {event} event - Browser event. + */ + 'unmaximize': (event: Event) => void; + /** + * Fired when a dialog drag completes. + * @param {event} event - Browser event. + */ + 'dragend': (event: Event) => void; +} + +declare class Dialog extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Dialog: GlobalComponentConstructor + } +} + +/** + * + * Dialog is a container to display content in an overlay window. + * + * Demos: + * + * - [Dialog](https://www.primefaces.org/primevue/showcase/#/dialog) + * + */ +export default Dialog; diff --git a/components/dialog/Dialog.spec.js b/components/dialog/Dialog.spec.js new file mode 100644 index 000000000..fc6cd874e --- /dev/null +++ b/components/dialog/Dialog.spec.js @@ -0,0 +1,46 @@ +import { mount } from '@vue/test-utils'; +import PrimeVue from '@/components/config/PrimeVue'; +import Dialog from './Dialog.vue'; + +describe('Dialog.vue', () => { + it('is Dialog element exist', async() => { + const wrapper = mount(Dialog, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true + } + }, + props: { + visible: false + } + }); + + expect(wrapper.find('.p-dialog.p-component').exists()).toBe(false); + + await wrapper.setProps({ visible: true }); + + expect(wrapper.find('.p-dialog.p-component').exists()).toBe(true); + }); + + it('slot checks', async() => { + const wrapper = mount(Dialog, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true + } + }, + props: { + visible: true + }, + slots: { + default: '

Lorem ipsum dolor sit amet, consectetur adipiscing elit

', + footer: '

Dialog Footer

' + } + }); + + expect(wrapper.find('.p-dialog-content').exists()).toBe(true); + expect(wrapper.find('.p-dialog-footer').exists()).toBe(true); + }); +}); \ No newline at end of file diff --git a/components/dialog/Dialog.vue b/components/dialog/Dialog.vue new file mode 100755 index 000000000..e3e434a5f --- /dev/null +++ b/components/dialog/Dialog.vue @@ -0,0 +1,600 @@ + + + + diff --git a/components/dialog/package.json b/components/dialog/package.json new file mode 100644 index 000000000..575095477 --- /dev/null +++ b/components/dialog/package.json @@ -0,0 +1,9 @@ +{ + "main": "./dialog.cjs.js", + "module": "./dialog.esm.js", + "unpkg": "./dialog.min.js", + "types": "./Dialog.d.ts", + "browser": { + "./sfc": "./Dialog.vue" + } +} \ No newline at end of file diff --git a/components/dialogservice/DialogService.d.ts b/components/dialogservice/DialogService.d.ts new file mode 100644 index 000000000..360b21fc1 --- /dev/null +++ b/components/dialogservice/DialogService.d.ts @@ -0,0 +1,27 @@ +import { Plugin } from 'vue'; +import { DynamicDialogOptions, DynamicDialogInstance } from '../dynamicdialogoptions'; + +declare const plugin: Plugin; +export default plugin; + +export interface DialogServiceMethods { + /** + * Displays the dialog using the dynamic dialog object options. + * @param {*} content - Dynamic component for content template + * @param {DynamicDialogOptions} options - DynamicDialog Object + * @return {@link DynamicDialogInstance} + */ + open: (content: any, options?: DynamicDialogOptions) => DynamicDialogInstance; +} + +declare module 'vue/types/vue' { + interface Vue { + $dialog: DialogServiceMethods; + } +} + +declare module '@vue/runtime-core' { + interface ComponentCustomProperties { + $dialog: DialogServiceMethods; + } +} diff --git a/components/dialogservice/DialogService.js b/components/dialogservice/DialogService.js new file mode 100644 index 000000000..08e9d5d3e --- /dev/null +++ b/components/dialogservice/DialogService.js @@ -0,0 +1,28 @@ +import { markRaw } from 'vue'; +import { PrimeVueDialogSymbol } from 'primevue/usedialog'; +import DynamicDialogEventBus from 'primevue/dynamicdialogeventbus'; + +export default { + install: (app) => { + const DialogService = { + open: (content, options) => { + const instance = { + content: content && markRaw(content), + options: options || {}, + data: options && options.data, + close: (params) => { + DynamicDialogEventBus.emit('close', { instance, params }); + } + } + + DynamicDialogEventBus.emit('open', { instance }); + + return instance; + } + }; + + app.config.unwrapInjectedRef = true; // Remove it after Vue 3.3. Details: https://vuejs.org/guide/components/provide-inject.html#working-with-reactivity + app.config.globalProperties.$dialog = DialogService; + app.provide(PrimeVueDialogSymbol, DialogService); + } +} diff --git a/components/dialogservice/package.json b/components/dialogservice/package.json new file mode 100644 index 000000000..830106d56 --- /dev/null +++ b/components/dialogservice/package.json @@ -0,0 +1,6 @@ +{ + "main": "./dialogservice.cjs.js", + "module": "./dialogservice.esm.js", + "unpkg": "./dialogservice.min.js", + "types": "./DialogService.d.ts" +} diff --git a/components/divider/Divider.d.ts b/components/divider/Divider.d.ts new file mode 100644 index 000000000..729fb6383 --- /dev/null +++ b/components/divider/Divider.d.ts @@ -0,0 +1,61 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type DividerHorizontalAlignType = 'left' | 'center' | 'right' | undefined; + +type DividerVerticalAlignType = 'top' | 'center' | 'bottom' | undefined; + +type DividerAlignType = DividerHorizontalAlignType | DividerVerticalAlignType | undefined; + +type DividerLayoutType = 'horizontal' | 'vertical' | undefined; + +type DividerType = 'solid' | 'dashed' | 'dotted' | undefined; + +export interface DividerProps { + /** + * Alignment of the content, options are 'left', 'center', 'right' for horizontal layout and 'top', 'center', 'bottom' for vertical. + * @see DividerAlignType + */ + align?: DividerAlignType; + /** + * Specifies the orientation, valid values are 'horizontal' and 'vertical'. + * @see DividerLayoutType + * Default value is 'horizontal'. + */ + layout?: DividerLayoutType; + /** + * Border style type. + * @see DividerType + * Default value is 'solid'. + */ + type?: DividerType; +} + +export interface DividerSlots { + /** + * Default content slot. + */ + default: () => VNode[]; +} + +export declare type DividerEmits = { +} + +declare class Divider extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Divider: GlobalComponentConstructor + } +} + +/** + * + * Divider is used to separate contents. + * + * Demos: + * + * - [Divider](https://www.primefaces.org/primevue/showcase/#/divider) + * + */ +export default Divider; diff --git a/components/divider/Divider.spec.js b/components/divider/Divider.spec.js new file mode 100644 index 000000000..4c0a1a98f --- /dev/null +++ b/components/divider/Divider.spec.js @@ -0,0 +1,26 @@ +import { mount } from '@vue/test-utils'; +import Divider from './Divider.vue'; + +describe('Divider.vue', () => { + it('should exist', () => { + const wrapper = mount(Divider); + + expect(wrapper.find('.p-divider.p-component').exists()).toBe(true); + expect(wrapper.find('.p-divider.p-component').classes()).toContain('p-divider-horizontal'); + expect(wrapper.find('.p-divider.p-component').classes()).toContain('p-divider-left'); + }); + + it('should exist', () => { + const wrapper = mount(Divider, { + props: { + layout: 'vertical', + align: 'center', + type: 'dashed' + } + }); + + expect(wrapper.find('.p-divider-vertical').exists()).toBe(true); + expect(wrapper.find('.p-divider-dashed').exists()).toBe(true); + expect(wrapper.find('.p-divider-center').exists()).toBe(true); + }); +}); \ No newline at end of file diff --git a/components/divider/Divider.vue b/components/divider/Divider.vue new file mode 100644 index 000000000..481c0d0f4 --- /dev/null +++ b/components/divider/Divider.vue @@ -0,0 +1,126 @@ + + + + + diff --git a/components/divider/package.json b/components/divider/package.json new file mode 100644 index 000000000..a03287086 --- /dev/null +++ b/components/divider/package.json @@ -0,0 +1,9 @@ +{ + "main": "./divider.cjs.js", + "module": "./divider.esm.js", + "unpkg": "./divider.min.js", + "types": "./Divider.d.ts", + "browser": { + "./sfc": "./Divider.vue" + } +} \ No newline at end of file diff --git a/components/dock/Dock.d.ts b/components/dock/Dock.d.ts new file mode 100644 index 000000000..68a2ab705 --- /dev/null +++ b/components/dock/Dock.d.ts @@ -0,0 +1,105 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; +import { MenuItem } from '../menuitem'; + +type DockPositionType = 'bottom' | 'top' | 'left' | 'right' | undefined; + +type DockTooltipEventType = 'hover' | 'focus' | undefined; + +export interface DockTooltipOptions { + /** + * Event to show the tooltip, valid values are hover and focus. + * @see DockTooltipEventType + */ + event: string; + /** + * Position of element. + * @see DockPositionType + * Default value is 'bottom'. + */ + position: string; + /** + * Optional options. + */ + [key: string]: string; +} + +export interface DockProps { + /** + * MenuModel instance to define the action items. + */ + model?: MenuItem[] | undefined; + /** + * Position of element. + * @see DockPositionType + * Default value is 'bottom'. + */ + position?: DockPositionType; + /** + * Style class of the element. + */ + class?: any; + /** + * Inline style of the element. + */ + style?: any; + /** + * Whether to apply 'router-link-active-exact' class if route exactly matches the item path. + * Default value is true. + */ + exact?: boolean | undefined; + /** + * Whether to display the tooltip on items. The modifiers of Tooltip can be used like an object in it. Valid keys are 'event' and 'position'. + * @see DockTooltipOptions + */ + tooltipOptions?: DockTooltipOptions; +} + +export interface DockSlots { + /** + * Custom item content. + * @param {Object} scope - item slot's params. + */ + item: (scope: { + /** + * Custom content for item. + */ + item: MenuItem; + }) => VNode[]; + /** + * Custom icon content. + * @param {Object} scope - icon slot's params. + */ + icon: (scope: { + /** + * Custom content for icon. + */ + item: MenuItem; + }) => VNode[]; +} + +export declare type DockEmits = { +} + +declare class Dock extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Dock: GlobalComponentConstructor + } +} + +/** + * + * Dock is a navigation component consisting of menuitems. + * + * Helper API: + * + * - [MenuItem](https://www.primefaces.org/primevue/showcase/#/menumodel) + * + * Demos: + * + * - [Dock](https://www.primefaces.org/primevue/showcase/#/dock) + * + */ +export default Dock; diff --git a/components/dock/Dock.vue b/components/dock/Dock.vue new file mode 100644 index 000000000..1f41aa9b9 --- /dev/null +++ b/components/dock/Dock.vue @@ -0,0 +1,143 @@ + + + + + diff --git a/components/dock/DockSub.vue b/components/dock/DockSub.vue new file mode 100644 index 000000000..320e5ba0e --- /dev/null +++ b/components/dock/DockSub.vue @@ -0,0 +1,104 @@ + + + diff --git a/components/dock/package.json b/components/dock/package.json new file mode 100644 index 000000000..a60167bef --- /dev/null +++ b/components/dock/package.json @@ -0,0 +1,9 @@ +{ + "main": "./dock.cjs.js", + "module": "./dock.esm.js", + "unpkg": "./dock.min.js", + "types": "./Dock.d.ts", + "browser": { + "./sfc": "./Dock.vue" + } + } \ No newline at end of file diff --git a/components/dropdown/Dropdown.d.ts b/components/dropdown/Dropdown.d.ts new file mode 100755 index 000000000..adaf626b8 --- /dev/null +++ b/components/dropdown/Dropdown.d.ts @@ -0,0 +1,416 @@ +import { HTMLAttributes, InputHTMLAttributes, VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; +import { VirtualScrollerProps, VirtualScrollerItemOptions } from '../virtualscroller'; + +type DropdownOptionLabelType = string | ((data: any) => string) | undefined; + +type DropdownOptionValueType = string | ((data: any) => any) | undefined; + +type DropdownOptionDisabledType = string | ((data: any) => boolean) | undefined; + +type DropdownOptionChildrenType = string | ((data: any) => any[]) | undefined; + +type DropdownFilterMatchModeType = 'contains' | 'startsWith' | 'endsWith' | undefined; + +type DropdownAppendToType = 'body' | 'self' | string | undefined | HTMLElement; + +export interface DropdownChangeEvent { + /** + * Browser event. + */ + originalEvent: Event; + /** + * Selected option value + */ + value: any; +} + +export interface DropdownFilterEvent { + /** + * Browser event. + */ + originalEvent: Event; + /** + * Filter value + */ + value: any; +} + +export interface DropdownProps { + /** + * Value of the component. + */ + modelValue?: any; + /** + * An array of selectitems to display as the available options. + */ + options?: any[]; + /** + * Property name or getter function to use as the label of an option. + * @see DropdownOptionLabelType + */ + optionLabel?: DropdownOptionLabelType; + /** + * Property name or getter function to use as the value of an option, defaults to the option itself when not defined. + * @see DropdownOptionValueType + */ + optionValue?: DropdownOptionValueType; + /** + * Property name or getter function to use as the disabled flag of an option, defaults to false when not defined. + * @see DropdownOptionDisabledType + */ + optionDisabled?: DropdownOptionDisabledType; + /** + * Property name or getter function to use as the label of an option group. + * @see DropdownOptionLabelType + */ + optionGroupLabel?: DropdownOptionLabelType; + /** + * Property name or getter function that refers to the children options of option group. + * @see DropdownOptionChildrenType + */ + optionGroupChildren?: DropdownOptionChildrenType; + /** + * Height of the viewport, a scrollbar is defined if height of list exceeds this value. + * Default value is '200px'. + */ + scrollHeight?: string | undefined; + /** + * When specified, displays a filter input at header. + */ + filter?: boolean | undefined; + /** + * Placeholder text to show when filter input is empty. + */ + filterPlaceholder?: string | undefined; + /** + * Locale to use in filtering. The default locale is the host environment's current locale. + */ + filterLocale?: string | undefined; + /** + * Defines the filtering algorithm to use when searching the options. + * @see DropdownFilterMatchModeType + * Default value is 'contains'. + */ + filterMatchMode?: DropdownFilterMatchModeType; + /** + * Fields used when filtering the options, defaults to optionLabel. + */ + filterFields?: string[] | undefined; + /** + * When present, custom value instead of predefined options can be entered using the editable input field. + */ + editable?: boolean | undefined; + /** + * Default text to display when no option is selected. + */ + placeholder?: string | undefined; + /** + * When present, it specifies that the component should be disabled. + */ + disabled?: boolean | undefined; + /** + * A property to uniquely identify an option. + */ + dataKey?: string | undefined; + /** + * When enabled, a clear icon is displayed to clear the value. + */ + showClear?: boolean | undefined; + /** + * Identifier of the underlying input element. + */ + inputId?: string | undefined; + /** + * Inline style of the input field. + */ + inputStyle?: any; + /** + * Style class of the input field. + */ + inputClass?: any; + /** + * Uses to pass all properties of the HTMLInputElement/HTMLSpanElement to the focusable input element inside the component. + */ + inputProps?: InputHTMLAttributes | HTMLAttributes | undefined; + /** + * Inline style of the overlay panel. + */ + panelStyle?: any; + /** + * Style class of the overlay panel. + */ + panelClass?: any; + /** + * Uses to pass all properties of the HTMLDivElement to the overlay panel inside the component. + */ + panelProps?: HTMLAttributes | undefined; + /** + * Uses to pass all properties of the HTMLInputElement to the filter input inside the component. + */ + filterInputProps?: InputHTMLAttributes | undefined; + /** + * Uses to pass all properties of the HTMLElement to the clear icon inside the component. + */ + clearIconProps?: HTMLAttributes | undefined; + /** + * A valid query selector or an HTMLElement to specify where the overlay gets attached. Special keywords are 'body' for document body and 'self' for the element itself. + * @see DropdownAppendToType + * Default value is 'body'. + */ + appendTo?: DropdownAppendToType; + /** + * Whether the dropdown is in loading state. + */ + loading?: boolean | undefined; + /** + * Icon to display in loading state. + * Default value is 'pi pi-spinner pi-spin'. + */ + loadingIcon?: string | undefined; + /** + * Whether to use the virtualScroller feature. The properties of VirtualScroller component can be used like an object in it. + * @see VirtualScroller.VirtualScrollerProps + */ + virtualScrollerOptions?: VirtualScrollerProps; + /** + * Whether to focus on the first visible or selected element when the overlay panel is shown. + * Default value is true. + */ + autoOptionFocus?: boolean | undefined; + /** + * Text to be displayed in hidden accessible field when filtering returns any results. Defaults to value from PrimeVue locale configuration. + * Default value is '{0} results are available'. + */ + filterMessage?: string | undefined; + /** + * Text to be displayed in hidden accessible field when options are selected. Defaults to value from PrimeVue locale configuration. + * Default value is '{0} items selected'. + */ + selectionMessage?: string | undefined; + /** + * Text to be displayed in hidden accessible field when any option is not selected. Defaults to value from PrimeVue locale configuration. + * Default value is 'No selected item'. + */ + emptySelectionMessage?: string | undefined; + /** + * Text to display when filtering does not return any results. Defaults to value from PrimeVue locale configuration. + * Default value is 'No results found'. + */ + emptyFilterMessage?: string | undefined; + /** + * Text to display when there are no options available. Defaults to value from PrimeVue locale configuration. + * Default value is 'No results found'. + */ + emptyMessage?: string | undefined; + /** + * Index of the element in tabbing order. + */ + tabindex?: number | string | undefined; + /** + * Defines a string value that labels an interactive element. + */ + "aria-label"?: string | undefined; + /** + * Identifier of the underlying input element. + */ + "aria-labelledby"?: string | undefined; +} + +export interface DropdownSlots { + /** + * Custom value template. + * @param {Object} scope - value slot's params. + */ + value: (scope: { + /** + * Value of the component + */ + value: any; + /** + * Placeholder prop value + */ + placeholder: string; + }) => VNode[]; + /** + * Custom indicator template. + */ + indicator: () => VNode[]; + /** + * Custom header template of panel. + * @param {Object} scope - header slot's params. + */ + header: (scope: { + /** + * Value of the component + */ + value: any; + /** + * Displayed options + */ + options: any[]; + }) => VNode[]; + /** + * Custom footer template of panel. + * @param {Object} scope - footer slot's params. + */ + footer: (scope: { + /** + * Value of the component + */ + value: any; + /** + * Displayed options + */ + options: any[]; + }) => VNode[]; + /** + * Custom option template. + * @param {Object} scope - option slot's params. + */ + option: (scope: { + /** + * Option instance + */ + option: any; + /** + * Index of the option + */ + index: number; + }) => VNode[]; + /** + * Custom option group template. + * @param {Object} scope - option group slot's params. + */ + optiongroup: (scope: { + /** + * Option instance + */ + option: any; + /** + * Index of the option + */ + index: number; + }) => VNode[]; + /** + * Custom empty filter template. + */ + emptyfilter: () => VNode[]; + /** + * Custom empty template. + */ + empty: () => VNode[]; + /** + * Custom content template. + * @param {Object} scope - content slot's params. + */ + content: (scope: { + /** + * An array of objects to display for virtualscroller + */ + items: any; + /** + * Style class of the component + */ + styleClass: string; + /** + * Referance of the content + * @param {HTMLElement} el - Element of 'ref' property + */ + contentRef(el: any): void; + /** + * Options of the items + * @param {number} index - Rendered index + * @return {@link VirtualScroller.VirtualScrollerItemOptions} + */ + getItemOptions(index: number): VirtualScrollerItemOptions; + }) => VNode[]; + /** + * Custom loader template. + * @param {Object} scope - loader slot's params. + */ + loader: (scope: { + /** + * Options of the loader items for virtualscroller + */ + options: any[]; + }) => VNode[]; +} + +export declare type DropdownEmits = { + /** + * Emitted when the value changes. + * @param {*} value - New value. + */ + 'update:modelValue': (value: any) => void; + /** + * Callback to invoke on value change. + * @param {DropdownChangeEvent} event - Custom change event. + */ + 'change': (event: DropdownChangeEvent) => void; + /** + * Callback to invoke when the component receives focus. + * @param {Event} event - Browser event. + */ + 'focus': (event: Event) => void; + /** + * Callback to invoke when the component loses focus. + * @param {Event} event - Browser event. + */ + 'blur': (event: Event) => void; + /** + * Callback to invoke before the overlay is shown. + */ + 'before-show': () => void; + /** + * Callback to invoke before the overlay is hidden. + */ + 'before-hide': () => void; + /** + * Callback to invoke when the overlay is shown. + */ + 'show': () => void; + /** + * Callback to invoke when the overlay is hidden. + */ + 'hide': () => void; + /** + * Callback to invoke on filter input. + * @param {DropdownFilterEvent} event - Custom filter event. + */ + 'filter': (event: DropdownFilterEvent) => void; +} + +declare class Dropdown extends ClassComponent { + /** + * Shows the overlay. + * @param {boolean} [isFocus] - Decides whether to focus on the component. Default value is false. + * + * @memberof Dropdown + */ + show: (isFocus?: boolean) => void; + /** + * Hides the overlay. + * @param {boolean} [isFocus] - Decides whether to focus on the component. Default value is false. + * + * @memberof Dropdown + */ + hide: (isFocus?: boolean) => void; +} + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Dropdown: GlobalComponentConstructor + } +} + +/** + * + * Dropdown is used to select an item from a list of options. + * + * Demos: + * + * - [Dropdown](https://www.primefaces.org/primevue/showcase/#/dropdown) + * + */ +export default Dropdown; diff --git a/components/dropdown/Dropdown.spec.js b/components/dropdown/Dropdown.spec.js new file mode 100644 index 000000000..80d28dcdd --- /dev/null +++ b/components/dropdown/Dropdown.spec.js @@ -0,0 +1,328 @@ +import { h } from 'vue'; +import { mount } from '@vue/test-utils'; +import PrimeVue from '@/components/config/PrimeVue'; +import Dropdown from '@/components/dropdown/Dropdown.vue'; + +describe('Dropdown.vue', () => { + let wrapper; + + beforeEach(async () => { + wrapper = mount(Dropdown, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true + } + } + }); + + await wrapper.trigger('click'); + }); + + it('should Dropdown exist', () => { + expect(wrapper.find('.p-dropdown.p-component').exists()).toBe(true); + expect(wrapper.find('.p-dropdown-panel').exists()).toBe(true); + expect(wrapper.find('.p-dropdown-empty-message').exists()).toBe(true); + expect(wrapper.find('.p-focus').exists()).toBe(true); + expect(wrapper.find('.p-inputwrapper-filled').exists()).toBe(false); + expect(wrapper.find('.p-inputwrapper-focus').exists()).toBe(true); + }) +}); + +describe('option checks', () => { + let wrapper; + + beforeEach(async() => { + wrapper = mount(Dropdown, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true + } + }, + props: { + options: [ + {name: 'New York', code: 'NY'}, + {name: 'Rome', code: 'RM'}, + {name: 'London', code: 'LDN'}, + {name: 'Istanbul', code: 'IST'}, + {name: 'Paris', code: 'PRS'} + ], + optionLabel: 'name', + optionValue: 'code', + placeholder: 'Select a City' + } + }); + + await wrapper.trigger('click'); + }); + + it('should show the options', () => { + expect(wrapper.find('.p-dropdown-label.p-placeholder').text()).toBe('Select a City'); + expect(wrapper.find('.p-dropdown-items-wrapper > .p-dropdown-items').exists()).toBe(true); + expect(wrapper.find('.p-dropdown-item').exists()).toBe(true); + expect(wrapper.findAll('.p-dropdown-item').length).toBe(5); + expect(wrapper.findAll('.p-dropdown-item')[0].text()).toBe('New York'); + }); +}); + +describe('editable checks', () => { + let wrapper; + + beforeEach(async() => { + wrapper = mount(Dropdown, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true + } + }, + props: { + options: [ + {name: 'New York', code: 'NY'}, + {name: 'Rome', code: 'RM'}, + {name: 'London', code: 'LDN'}, + {name: 'Istanbul', code: 'IST'}, + {name: 'Paris', code: 'PRS'} + ], + optionLabel: 'name', + optionValue: 'code', + placeholder: 'Select a City', + editable: true + } + }); + + await wrapper.trigger('click'); + }); + + it('should show the options', () => { + expect(wrapper.find('.p-dropdown-label.p-placeholder').exists()).toBe(false); + expect(wrapper.find('.p-dropdown-label.p-inputtext').exists()).toBe(true); + }); +}); + +describe('option groups checks', () => { + let wrapper; + + beforeEach(async() => { + wrapper = mount(Dropdown, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true + } + }, + props: { + options: [ + { + label: 'Germany', code: 'DE', + items: [ + {label: 'Berlin', value: 'Berlin'}, + {label: 'Frankfurt', value: 'Frankfurt'}, + {label: 'Hamburg', value: 'Hamburg'}, + {label: 'Munich', value: 'Munich'} + ] + }, + { + label: 'USA', code: 'US', + items: [ + {label: 'Chicago', value: 'Chicago'}, + {label: 'Los Angeles', value: 'Los Angeles'}, + {label: 'New York', value: 'New York'}, + {label: 'San Francisco', value: 'San Francisco'} + ] + }, + { + label: 'Japan', code: 'JP', + items: [ + {label: 'Kyoto', value: 'Kyoto'}, + {label: 'Osaka', value: 'Osaka'}, + {label: 'Tokyo', value: 'Tokyo'}, + {label: 'Yokohama', value: 'Yokohama'} + ] + } + ], + optionLabel: 'label', + optionGroupLabel: 'label', + optionGroupChildren: 'items' + } + }); + + await wrapper.trigger('click'); + }); + + it('should show the option groups', () => { + expect(wrapper.findAll('.p-dropdown-item-group').length).toBe(3); + expect(wrapper.findAll('.p-dropdown-item-group')[0].text()).toBe('Germany'); + }); +}); + +describe('templating checks', () => { + let wrapper; + + beforeEach(async() => { + wrapper = mount(Dropdown, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true + } + }, + slots: { + header: h('span', {class: 'header-slot'}, 'Header slot'), + footer: h('span', {class: 'footer-slot'}, 'Footer slot'), + option: h('span', {class: 'option-slot'}, 'Option slot'), + optiongroup: h('span', {class: 'optiongroup-slot'}, 'OptionGroup slot'), + emptyfilter: h('span', {class: 'emptyfilter-slot'}, 'Empty filter slot') + }, + props: { + options: [ + { + label: 'Germany', code: 'DE', + items: [ + {label: 'Berlin', value: 'Berlin'}, + {label: 'Frankfurt', value: 'Frankfurt'}, + {label: 'Hamburg', value: 'Hamburg'}, + {label: 'Munich', value: 'Munich'} + ] + } + ], + optionLabel: 'label', + optionGroupLabel: 'label', + optionGroupChildren: 'items' + } + }); + + await wrapper.trigger('click'); + }); + + it('should see header and footer slots', () => { + expect(wrapper.find('.header-slot').exists()).toBe(true); + expect(wrapper.find('.header-slot').text()).toBe('Header slot'); + expect(wrapper.find('.footer-slot').exists()).toBe(true); + expect(wrapper.find('.footer-slot').text()).toBe('Footer slot'); + expect(wrapper.find('.option-slot').exists()).toBe(true); + expect(wrapper.find('.option-slot').text()).toBe('Option slot'); + expect(wrapper.find('.optiongroup-slot').exists()).toBe(true); + expect(wrapper.find('.optiongroup-slot').text()).toBe('OptionGroup slot'); + }); +}); + +describe('empty templating checks', () => { + let wrapper; + + beforeEach(async() => { + wrapper = mount(Dropdown, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true + } + }, + props: { + options: [], + optionLabel: 'label', + optionGroupLabel: 'label', + optionGroupChildren: 'items', + emptyMessage: 'Need options prop', + filterValue: 'xd' + } + }); + + await wrapper.trigger('click'); + }); + + it('should see empty slots', () => { + expect(wrapper.find('.p-dropdown-empty-message').exists()).toBe(true); + expect(wrapper.find('.p-dropdown-empty-message').text()).toBe('Need options prop'); + }); +}); + +describe('loader checks', () => { + let wrapper; + + beforeEach(async() => { + wrapper = mount(Dropdown, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true + } + }, + props: { + loading: true, + loadingIcon: 'pi pi-discord', + options: [ + {name: 'New York', code: 'NY'}, + {name: 'Rome', code: 'RM'}, + {name: 'London', code: 'LDN'}, + {name: 'Istanbul', code: 'IST'}, + {name: 'Paris', code: 'PRS'} + ], + optionLabel: 'name', + optionValue: 'code', + placeholder: 'Select a City' + } + }); + + await wrapper.trigger('click'); + }); + + it('should show the loader', async () => { + expect(wrapper.find('.p-dropdown-trigger-icon').classes()).toContain('pi-discord'); + + await wrapper.setProps({ loading: false }); + + expect(wrapper.find('.p-dropdown-trigger-icon').classes()).not.toContain('pi-discord'); + }); +}); + +describe('filter checks', () => { + let wrapper; + + beforeEach(async() => { + wrapper = mount(Dropdown, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true + } + }, + props: { + filter: true, + options: [ + {name: 'Australia', code: 'AU'}, + {name: 'Brazil', code: 'BR'}, + {name: 'China', code: 'CN'}, + {name: 'Egypt', code: 'EG'}, + {name: 'France', code: 'FR'}, + {name: 'Germany', code: 'DE'}, + {name: 'India', code: 'IN'}, + {name: 'Japan', code: 'JP'}, + {name: 'Spain', code: 'ES'}, + {name: 'United States', code: 'US'} + ], + optionLabel: 'name' + } + }); + + await wrapper.trigger('click'); + }); + + it('should make filtering', async () => { + const filterInput = wrapper.find('.p-dropdown-filter'); + expect(filterInput.exists()).toBe(true); + + const event = {'target':{'value':'c'}}; + const onFilterChange = jest.spyOn(wrapper.vm, 'onFilterChange'); + + wrapper.vm.onFilterChange(event); + await wrapper.vm.$nextTick(); + + expect(onFilterChange).toHaveBeenCalled(); + + await wrapper.setData({filterValue: 'c'}); + + expect(wrapper.findAll('.p-dropdown-item').length).toBe(2); + }); +}); \ No newline at end of file diff --git a/components/dropdown/Dropdown.vue b/components/dropdown/Dropdown.vue new file mode 100755 index 000000000..55e37ae96 --- /dev/null +++ b/components/dropdown/Dropdown.vue @@ -0,0 +1,975 @@ + + + + + diff --git a/components/dropdown/package.json b/components/dropdown/package.json new file mode 100644 index 000000000..101b4f8c9 --- /dev/null +++ b/components/dropdown/package.json @@ -0,0 +1,9 @@ +{ + "main": "./dropdown.cjs.js", + "module": "./dropdown.esm.js", + "unpkg": "./dropdown.min.js", + "types": "./Dropdown.d.ts", + "browser": { + "./sfc": "./Dropdown.vue" + } +} \ No newline at end of file diff --git a/components/dynamicdialog/DynamicDialog.d.ts b/components/dynamicdialog/DynamicDialog.d.ts new file mode 100644 index 000000000..d15d27ba6 --- /dev/null +++ b/components/dynamicdialog/DynamicDialog.d.ts @@ -0,0 +1,26 @@ +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface DynamicDialogProps {} + +export declare type DynamicDialogEmits = {} + +export interface DynamicDialogSlots {} + +declare class DynamicDialog extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + DynamicDialog: GlobalComponentConstructor + } +} + +/** + * + * DynamicDialogs can be created dynamically with any component as the content using a DialogService. + * + * Demos: + * + * - [DynamicDialog](https://www.primefaces.org/primevue/showcase/#/dynamicdialog) + * + */ +export default DynamicDialog; diff --git a/components/dynamicdialog/DynamicDialog.vue b/components/dynamicdialog/DynamicDialog.vue new file mode 100644 index 000000000..7dbe4b40f --- /dev/null +++ b/components/dynamicdialog/DynamicDialog.vue @@ -0,0 +1,75 @@ + + + diff --git a/components/dynamicdialog/package.json b/components/dynamicdialog/package.json new file mode 100644 index 000000000..c0fc64e5d --- /dev/null +++ b/components/dynamicdialog/package.json @@ -0,0 +1,9 @@ +{ + "main": "./dynamicdialog.cjs.js", + "module": "./dynamicdialog.esm.js", + "unpkg": "./dynamicdialog.min.js", + "types": "./DynamicDialog.d.ts", + "browser": { + "./sfc": "./DynamicDialog.vue" + } +} diff --git a/components/dynamicdialogeventbus/DynamicDialogEventBus.js b/components/dynamicdialogeventbus/DynamicDialogEventBus.js new file mode 100644 index 000000000..602940783 --- /dev/null +++ b/components/dynamicdialogeventbus/DynamicDialogEventBus.js @@ -0,0 +1,3 @@ +import { EventBus } from 'primevue/utils'; + +export default EventBus(); diff --git a/components/dynamicdialogeventbus/package.json b/components/dynamicdialogeventbus/package.json new file mode 100644 index 000000000..c23adbd52 --- /dev/null +++ b/components/dynamicdialogeventbus/package.json @@ -0,0 +1,5 @@ +{ + "main": "./dynamicdialogeventbus.cjs.js", + "module": "./dynamicdialogeventbus.esm.js", + "unpkg": "./dynamicdialogeventbus.min.js" +} diff --git a/components/dynamicdialogoptions/DynamicDialogOptions.d.ts b/components/dynamicdialogoptions/DynamicDialogOptions.d.ts new file mode 100644 index 000000000..c6632974b --- /dev/null +++ b/components/dynamicdialogoptions/DynamicDialogOptions.d.ts @@ -0,0 +1,73 @@ +import { DialogProps } from '../dialog'; + +export type DynamicDialogCloseType = 'config-close' | 'dialog-close' | undefined; + +export interface DynamicDialogTemplates { + /** + * Custom header template. + */ + header?: any; + /** + * Custom footer template. + */ + footer?: any; +} + +export interface DynamicDialogCloseOptions { + /** + * Custom data object + */ + data?: any; + /** + * Close type + * @see DynamicDialogCloseType + */ + type: DynamicDialogCloseType; +} + +export interface DynamicDialogOptions { + /** + * Dialog Props + * @see DialogProps + */ + props?: DialogProps; + /** + * Dialog Slots + * @see DynamicDialogTemplates + */ + templates?: DynamicDialogTemplates; + /** + * Custom data object + */ + data?: any; + /** + * Closes the dialog. + * @see DynamicDialogCloseOptions + */ + onClose?(options?: DynamicDialogCloseOptions): void; + /** + * Optional + */ + [key: string]: any; +} + +export interface DynamicDialogInstance { + /** + * Dynamic component for content template + */ + content: any; + /** + * Instance options + * @see DynamicDialogOptions + */ + options: DynamicDialogOptions; + /** + * Custom data object + */ + data: any; + /** + * Closes the dialog. + * @param {*} params - Parameters sent by the user to the root instance + */ + close: (params?: any) => void; +} diff --git a/components/dynamicdialogoptions/package.json b/components/dynamicdialogoptions/package.json new file mode 100644 index 000000000..2f003bf5c --- /dev/null +++ b/components/dynamicdialogoptions/package.json @@ -0,0 +1,3 @@ +{ + "types": "./DynamicDialogOptions.d.ts" +} diff --git a/components/editor/Editor.d.ts b/components/editor/Editor.d.ts new file mode 100755 index 000000000..30a4f23ab --- /dev/null +++ b/components/editor/Editor.d.ts @@ -0,0 +1,119 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface EditorTextChangeEvent { + /** + * Current value as html. + */ + htmlValue: string; + /** + * Current value as text. + */ + textValue: any; + /** + * Representation of the change. + */ + delta: any; + /** + * Source of change. Will be either 'user' or 'api'. + */ + source: string; + /** + * Text editor instance. + */ + instance: any; +} + +export interface EditorSelectionChangeEvent { + /** + * Current value as html. + */ + htmlValue: string; + /** + * Current value as text. + */ + textValue: any; + /** + * Representation of the selection boundaries. + */ + range: any; + /** + * Representation of the previous selection boundaries. + */ + oldRange: any; + /** + * Source of change. Will be either 'user' or 'api'. + */ + source: string; + /** + * Text editor instance. + */ + instance: any; +} + +export interface EditorProps { + /** + * Value of the content. + */ + modelValue?: string | undefined; + /** + * Placeholder text to show when editor is empty. + */ + placeholder?: string | undefined; + /** + * Whether to instantiate the editor to readonly mode. + */ + readonly?: boolean | undefined; + /** + * Whitelist of formats to display, see [here](https://quilljs.com/docs/formats/) for available options. + */ + formats?: any[]; + /** + * Inline style of the container. + */ + editorStyle?: any; +} + +export interface EditorSlots { + /** + * Custom toolbar template. + */ + toolbar: () => VNode[]; +} + +export declare type EditorEmits = { + /** + * Emitted when the value changes. + * @param {string} value - New value. + */ + 'update:modelValue': (value: string) => void; + /** + * Callback to invoke when text of editor changes. + * @param {EditorTextChangeEvent} event - Custom text change event. + */ + 'text-change': (event: EditorTextChangeEvent) => void; + /** + * Callback to invoke when selection of the text changes. + * @param {EditorSelectionChangeEvent} event - Custom selection change event. + */ + 'selection-change': (event: EditorSelectionChangeEvent) => void; +} + +declare class Editor extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Editor: GlobalComponentConstructor + } +} + +/** + * + * Editor is rich text editor component based on Quill. + * + * Demos: + * + * - [Editor](https://www.primefaces.org/primevue/showcase/#/editor) + * + */ +export default Editor; diff --git a/components/editor/Editor.vue b/components/editor/Editor.vue new file mode 100755 index 000000000..9137581c6 --- /dev/null +++ b/components/editor/Editor.vue @@ -0,0 +1,1079 @@ + + + + + diff --git a/components/editor/package.json b/components/editor/package.json new file mode 100644 index 000000000..dc6386c2b --- /dev/null +++ b/components/editor/package.json @@ -0,0 +1,9 @@ +{ + "main": "./editor.cjs.js", + "module": "./editor.esm.js", + "unpkg": "./editor.min.js", + "types": "./Editor.d.ts", + "browser": { + "./sfc": "./Editor.vue" + } +} \ No newline at end of file diff --git a/components/fieldset/Fieldset.d.ts b/components/fieldset/Fieldset.d.ts new file mode 100755 index 000000000..486f0757f --- /dev/null +++ b/components/fieldset/Fieldset.d.ts @@ -0,0 +1,71 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface FieldsetToggleEvent { + /** + * Browser event. + */ + originalEvent: Event; + /** + * Collapsed state as a boolean + */ + value: boolean; +} + +export interface FieldsetProps { + /** + * Header text of the fieldset. + */ + legend?: string | undefined; + /** + * When specified, content can toggled by clicking the legend. + */ + toggleable?: boolean | undefined; + /** + * Defines the default visibility state of the content. + */ + collapsed?: boolean | undefined; +} + +export interface FieldsetSlots { + /** + * Default content slot. + */ + default: () => VNode[]; + /** + * Custom legend template. + */ + legend: () => VNode[]; +} + +export declare type FieldsetEmits = { + /** + * Emitted when the collapsed changes. + * @param {boolean} value - New value. + */ + 'update:collapsed': (value: boolean) => void; + /** + * Callback to invoke when a tab gets expanded or collapsed. + * @param {FieldsetToggleEvent} event - Custom toggle event. + */ + 'toggle': (event: FieldsetToggleEvent) => void; +} + +declare class Fieldset extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Fieldset: GlobalComponentConstructor
+ } +} + +/** + * + * Fieldset is a grouping component with the optional content toggle feature. + * + * Demos: + * + * - [Fieldset](https://www.primefaces.org/primevue/showcase/#/fieldset) + * + */ +export default Fieldset; diff --git a/components/fieldset/Fieldset.spec.js b/components/fieldset/Fieldset.spec.js new file mode 100644 index 000000000..2a5c495c5 --- /dev/null +++ b/components/fieldset/Fieldset.spec.js @@ -0,0 +1,31 @@ +import { mount } from '@vue/test-utils'; +import Fieldset from './Fieldset.vue'; + +describe('Fieldset.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Fieldset, { + props: { + legend: 'Header', + toggleable: true, + collapsed: true + }, + slots: { + default: `

Lorem ipsum dolor sit amet, consectetur adipiscing elit

` + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-fieldset.p-component').exists()).toBe(true); + expect(wrapper.find('.p-fieldset-legend').exists()).toBe(true); + expect(wrapper.find('.p-toggleable-content').exists()).toBe(true); + }); + + it('toggleable check', async () => { + await wrapper.setProps({ collapsed: false }); + wrapper.vm.toggle({}); + expect(wrapper.emitted()['update:collapsed'][0]).toEqual([true]); + }); +}); \ No newline at end of file diff --git a/components/fieldset/Fieldset.vue b/components/fieldset/Fieldset.vue new file mode 100755 index 000000000..ca8feaa4a --- /dev/null +++ b/components/fieldset/Fieldset.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/components/fieldset/package.json b/components/fieldset/package.json new file mode 100644 index 000000000..58216c297 --- /dev/null +++ b/components/fieldset/package.json @@ -0,0 +1,9 @@ +{ + "main": "./fieldset.cjs.js", + "module": "./fieldset.esm.js", + "unpkg": "./fieldset.min.js", + "types": "./Fieldset.d.ts", + "browser": { + "./sfc": "./Fieldset.vue" + } +} \ No newline at end of file diff --git a/components/fileupload/FileUpload.d.ts b/components/fileupload/FileUpload.d.ts new file mode 100755 index 000000000..24f3aef6c --- /dev/null +++ b/components/fileupload/FileUpload.d.ts @@ -0,0 +1,274 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type FileUploadModeType = 'advanced' | 'basic' | undefined; + +export interface FileUploadSelectEvent { + /** + * Browser event. + */ + originalEvent: Event; + /** + * List of selected files. + */ + files: any; +} + +export interface FileUploadBeforeUploadEvent { + /** + * XmlHttpRequest instance. + */ + xhr: XMLHttpRequest; + /** + * FormData object. + */ + formData: FormData; +} + +export interface FileUploadProgressEvent { + /** + * Browser event. + */ + originalEvent: Event; + /** + * Calculated progress value. + */ + progress: number; +} + +export interface FileUploadUploadEvent { + /** + * XmlHttpRequest instance. + */ + xhr: XMLHttpRequest; + /** + * Files that are not uploaded. + */ + files: File | File[]; +} + +export interface FileUploadUploaderEvent { + /** + * List of selected files. + */ + files: File | File[]; +} + +export interface FileUploadErrorEvent { + /** + * XmlHttpRequest instance. + */ + xhr: XMLHttpRequest; + /** + * Files that are not uploaded. + */ + files: File | File[]; +} + +export interface FileUploadBeforeSendEvent { + /** + * XmlHttpRequest instance. + */ + xhr: XMLHttpRequest; + /** + * FormData object. + */ + formData: FormData; +} + +export interface FileUploadRemoveEvent { + /** + * Removed file. + */ + file: File; + /** + * Remaining files to be uploaded. + */ + files: File[]; +} + +export interface FileUploadProps { + /** + * Name of the request parameter to identify the files at backend. + */ + name?: string | undefined; + /** + * Remote url to upload the files. + */ + url?: string | undefined; + /** + * Defines the UI of the component, possible values are 'advanced' and 'basic'. + * @see FileUploadModeType + * Default value is 'advanced'. + */ + mode?: FileUploadModeType; + /** + * Used to select multiple files at once from file dialog. + */ + multiple?: boolean | undefined; + /** + * Pattern to restrict the allowed file types such as 'image/*'. + */ + accept?: string | undefined; + /** + * Disables the upload functionality. + */ + disabled?: boolean | undefined; + /** + * When enabled, upload begins automatically after selection is completed. + */ + auto?: boolean | undefined; + /** + * Maximum file size allowed in bytes. + */ + maxFileSize?: number | undefined; + /** + * Message of the invalid fize size. + * Default value is '{0}: Invalid file size, file size should be smaller than {1}.'. + */ + invalidFileSizeMessage?: string | undefined; + /** + * Message to display when number of files to be uploaded exceeeds the limit. + * Default value is 'Maximum number of files exceeded, limit is {0} at most.'. + */ + invalidFileLimitMessage?: string | undefined; + /** + * Message of the invalid fize type. + * Default value is '{0}: Invalid file type, allowed file types: {1}.'. + */ + invalidFileTypeMessage?: string | undefined; + /** + * Maximum number of files that can be uploaded. + */ + fileLimit?: number | undefined; + /** + * Cross-site Access-Control requests should be made using credentials such as cookies, authorization headers or TLS client certificates. + */ + withCredentials?: boolean | undefined; + /** + * Width of the image thumbnail in pixels. + * Default value is 50. + */ + previewWidth?: number | undefined; + /** + * Label of the choose button. Defaults to PrimeVue Locale configuration. + */ + chooseLabel?: string | undefined; + /** + * Label of the upload button. Defaults to PrimeVue Locale configuration. + * Default value is 'Upload'. + */ + uploadLabel?: string | undefined; + /** + * Label of the cancel button. Defaults to PrimeVue Locale configuration. + * Default value is 'Cancel'. + */ + cancelLabel?: string | undefined; + /** + * Whether to use the default upload or a manual implementation defined in uploadHandler callback. Defaults to PrimeVue Locale configuration. + */ + customUpload?: boolean | undefined; + /** + * Whether to show the upload button. + * Default value is true. + */ + showUploadButton?: boolean | undefined; + /** + * Whether to show the cancel button. + * Default value is true. + */ + showCancelButton?: boolean | undefined; + /** + * Icon of the choose button. + */ + chooseIcon?: string | undefined; + /** + * Icon of the upload button. + */ + uploadIcon?: string | undefined; + /** + * Icon of the cancel button. + */ + cancelIcon?: string | undefined; + /** + * Inline style of the component. + */ + style?: any; + /** + * Style class of the component. + */ + class?: any; +} + +export interface FileUploadSlots { + /** + * Custom empty template. + */ + empty: () => VNode[]; +} + +export declare type FileUploadEmits = { + /** + * Callback to invoke when files are selected. + * @param {FileUploadSelectEvent} event - Custom select event. + */ + 'select': (event: FileUploadSelectEvent) => void; + /** + * Callback to invoke before file upload begins to customize the request such as post parameters before the files. + * @param {FileUploadBeforeUploadEvent} event - Custom before upload event. + */ + 'before-upload': (event: FileUploadBeforeUploadEvent) => void; + /** + * Callback to invoke when files are being uploaded. + * @param {FileUploadProgressEvent} event - Custom progress event. + */ + 'progress': (event: FileUploadProgressEvent) => void; + /** + * Callback to invoke when file upload is complete. + * @param {FileUploadUploadEvent} event - Custom upload event. + */ + 'upload': (event: FileUploadUploadEvent) => void; + /** + * Callback to invoke to implement a custom upload. + * @param {FileUploadUploaderEvent} event - Custom uploader event. + */ + 'uploader': (event: FileUploadUploaderEvent) => void; + /** + * Callback to invoke if file upload fails. + * @param {FileUploadErrorEvent} event - Custom error event. + */ + 'error': (event: FileUploadErrorEvent) => void; + /** + * Callback to invoke before file send begins to customize the request such as adding headers. + * @param {FileUploadBeforeSendEvent} event - Custom before send event. + */ + 'before-send': (event: FileUploadBeforeSendEvent) => void; + /** + * Callback to invoke when files in queue are removed without uploading. + */ + 'clear': () => void; + /** + * Callback to invoke when a singe file is removed from the list. + * @param {FileUploadRemoveEvent} event - Custom remove event. + */ + 'remove': (event: FileUploadRemoveEvent) => void; +} + +declare class FileUpload extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + FileUpload: GlobalComponentConstructor + } +} + +/** + * + * FileUpload is an advanced uploader with dragdrop support, multi file uploads, auto uploading, progress tracking and validations. + * + * Demos: + * + * - [FileUpload](https://www.primefaces.org/primevue/showcase/#/fileupload) + * + */ +export default FileUpload; diff --git a/components/fileupload/FileUpload.vue b/components/fileupload/FileUpload.vue new file mode 100755 index 000000000..5b9a500b0 --- /dev/null +++ b/components/fileupload/FileUpload.vue @@ -0,0 +1,529 @@ + + + + + diff --git a/components/fileupload/package.json b/components/fileupload/package.json new file mode 100644 index 000000000..6e5a80e23 --- /dev/null +++ b/components/fileupload/package.json @@ -0,0 +1,9 @@ +{ + "main": "./fileupload.cjs.js", + "module": "./fileupload.esm.js", + "unpkg": "./fileupload.min.js", + "types": "./FileUpload.d.ts", + "browser": { + "./sfc": "./FileUpload.vue" + } +} \ No newline at end of file diff --git a/components/fullcalendar/FullCalendar.d.ts b/components/fullcalendar/FullCalendar.d.ts new file mode 100755 index 000000000..4df696a2b --- /dev/null +++ b/components/fullcalendar/FullCalendar.d.ts @@ -0,0 +1,36 @@ +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface FullCalendarProps { + events?: any[]; + options?: object; +} + +export interface FullCalendarSlots { +} + +export declare type FullCalendarEmits = { +} + +declare class FullCalendar extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + FullCalendar: GlobalComponentConstructor + } +} + +/** + * + * PrimeVue provides theming for the FullCalendar Vue component. + * + * Helper API: + * + * - [FullCalendar](https://fullcalendar.io/docs/vue) + * + * Demos: + * + * - [FullCalendar](https://www.primefaces.org/primevue/showcase/#/fullcalendar) + * + * @deprecated + */ +export default FullCalendar; diff --git a/components/fullcalendar/FullCalendar.vue b/components/fullcalendar/FullCalendar.vue new file mode 100755 index 000000000..94efb5a8b --- /dev/null +++ b/components/fullcalendar/FullCalendar.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/components/fullcalendar/package.json b/components/fullcalendar/package.json new file mode 100644 index 000000000..08cae042c --- /dev/null +++ b/components/fullcalendar/package.json @@ -0,0 +1,9 @@ +{ + "main": "./fullcalendar.cjs.js", + "module": "./fullcalendar.esm.js", + "unpkg": "./fullcalendar.min.js", + "types": "./FullCalendar.d.ts", + "browser": { + "./sfc": "./FullCalendar.vue" + } +} \ No newline at end of file diff --git a/components/galleria/Galleria.d.ts b/components/galleria/Galleria.d.ts new file mode 100755 index 000000000..5c65fd52d --- /dev/null +++ b/components/galleria/Galleria.d.ts @@ -0,0 +1,210 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type GalleriaThumbnailsPositionType = 'bottom' | 'top' | 'left' | 'right' | undefined; + +type GalleriaIndicatorsPositionType = 'bottom' | 'top' | 'left' | 'right' | undefined; + +export interface GalleriaResponsiveOptions { + /** + * Breakpoint for responsive mode. Exp; @media screen and (max-width: ${breakpoint}) {...} + */ + breakpoint: string; + /** + * The number of visible items on breakpoint. + */ + numVisible: number; +} + +export interface GalleriaProps { + /** + * Unique identifier of the element. + */ + id?: string | undefined; + /** + * An array of objects to display. + */ + value?: any[]; + /** + * Index of the first item. + * Default value is 0. + */ + activeIndex?: number | undefined; + /** + * Whether to display the component on fullscreen. + */ + fullScreen?: boolean | undefined; + /** + * Specifies the visibility of the mask on fullscreen mode. + */ + visible?: boolean | undefined; + /** + * Number of items per page. + */ + numVisible?: number | undefined; + /** + * An array of options for responsive design. + * @see GalleriaResponsiveOptions + */ + responsiveOptions?: GalleriaResponsiveOptions[]; + /** + * Whether to display navigation buttons in item section. + */ + showItemNavigators?: boolean | undefined; + /** + * Whether to display navigation buttons in thumbnail container. + * Default value is true. + */ + showThumbnailNavigators?: boolean | undefined; + /** + * Whether to display navigation buttons on item hover. + */ + showItemNavigatorsOnHover?: boolean | undefined; + /** + * When enabled, item is changed on indicator hover. + */ + changeItemOnIndicatorHover?: boolean | undefined; + /** + * Defines if scrolling would be infinite. + */ + circular?: boolean | undefined; + /** + * Items are displayed with a slideshow in autoPlay mode. + */ + autoPlay?: boolean | undefined; + /** + * Time in milliseconds to scroll items. + * Default value is 4000. + */ + transitionInterval?: number | undefined; + /** + * Whether to display thumbnail container. + * Default value is true. + */ + showThumbnails?: boolean | undefined; + /** + * Position of thumbnails. + * @see GalleriaThumbnailsPositionType + * Default value is 'bottom'. + */ + thumbnailsPosition?: GalleriaThumbnailsPositionType; + /** + * Height of the viewport in vertical thumbnail. + * Default value is '300px'. + */ + verticalThumbnailViewPortHeight?: string | undefined; + /** + * Whether to display indicator container. + */ + showIndicators?: boolean | undefined; + /** + * When enabled, indicator container is displayed on item container. + */ + showIndicatorsOnItem?: boolean | undefined; + /** + * Position of indicators. + * @see GalleriaIndicatorsPositionType + * Default value is 'bottom'. + */ + indicatorsPosition?: GalleriaIndicatorsPositionType; + /** + * Base zIndex value to use in layering. + * Default value is 0. + */ + baseZIndex?: number | undefined; + /** + * Style class of the mask on fullscreen mode. + */ + maskClass?: string | undefined; + /** + * Inline style of the component on fullscreen mode. Otherwise, the 'style' property can be used. + */ + containerStyle?: any; + /** + * Style class of the component on fullscreen mode. Otherwise, the 'class' property can be used. + */ + containerClass?: any; +} + +export interface GalleriaSlots { + /** + * Custom header template. + */ + header: () => VNode[]; + /** + * Custom footer template. + */ + footer: () => VNode[]; + /** + * Custom item template. + * @param {Object} scope - item slot's params. + */ + item: (scope: { + /** + * Item instance + */ + item: any; + }) => VNode[]; + /** + * Custom caption template. + * @param {Object} scope - caption slot's params. + */ + caption: (scope: { + /** + * Item instance + */ + item: any; + }) => VNode[]; + /** + * Custom indicator template. + * @param {Object} scope - indicator slot's params. + */ + indicator: (scope: { + /** + * Index of the indicator item + */ + index: number; + }) => VNode[]; + /** + * Custom thumbnail template. + * @param {Object} scope - thumbnail slot's params. + */ + thumbnail: (scope: { + /** + * Item instance + */ + item: any; + }) => VNode[]; +} + +export declare type GalleriaEmits = { + /** + * Emitted when the active index changes. + * @param {number} value - Index of new active item. + */ + 'update:activeIndex': (value: number) => void; + /** + * Emitted when the visible changes. + * @param {boolean} value - New value. + */ + 'update:visible': (value: boolean) => void; +} + +declare class Galleria extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Galleria: GlobalComponentConstructor + } +} + +/** + * + * Galleria is an advanced content gallery component. + * + * Demos: + * + * - [Galleria](https://www.primefaces.org/primevue/showcase/#/galleria) + * + */ +export default Galleria; diff --git a/components/galleria/Galleria.spec.js b/components/galleria/Galleria.spec.js new file mode 100644 index 000000000..ba5e09539 --- /dev/null +++ b/components/galleria/Galleria.spec.js @@ -0,0 +1,65 @@ +import { mount } from '@vue/test-utils'; +import PrimeVue from '@/components/config/PrimeVue'; +import Galleria from './Galleria.vue'; + +describe('Gallleria.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Galleria, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true + } + }, + props: { + value: [ + { + "itemImageSrc": "demo/images/galleria/galleria1.jpg", + "thumbnailImageSrc": "demo/images/galleria/galleria1s.jpg", + "alt": "Description for Image 1", + "title": "Title 1" + }, + { + "itemImageSrc": "demo/images/galleria/galleria2.jpg", + "thumbnailImageSrc": "demo/images/galleria/galleria2s.jpg", + "alt": "Description for Image 2", + "title": "Title 2" + }, + { + "itemImageSrc": "demo/images/galleria/galleria3.jpg", + "thumbnailImageSrc": "demo/images/galleria/galleria3s.jpg", + "alt": "Description for Image 3", + "title": "Title 3" + } + ], + numVisible: 2 + }, + slots: { + item: ` + + ` + } + }); + }); + + it('should exist',() => { + expect(wrapper.find('.p-galleria.p-component').exists()).toBe(true); + expect(wrapper.findAll('.p-galleria-item-container > .p-galleria-item').length).toBe(1); + expect(wrapper.findAll('.p-galleria-thumbnail-item-active').length).toBe(2); + }); + + it('should active item change', async() => { + expect(wrapper.find('.p-galleria-item-container img').attributes().alt).toBe('Description for Image 1'); + + wrapper.vm.onActiveItemChange(1); + await wrapper.setProps({activeIndex: 1}); + + expect(wrapper.find('.p-galleria-item-container img').attributes().alt).toBe('Description for Image 2'); + expect(wrapper.emitted()['update:activeIndex'][0]).toEqual([1]); + + }); +}); \ No newline at end of file diff --git a/components/galleria/Galleria.vue b/components/galleria/Galleria.vue new file mode 100755 index 000000000..cf320b8be --- /dev/null +++ b/components/galleria/Galleria.vue @@ -0,0 +1,465 @@ + + + + + diff --git a/components/galleria/GalleriaContent.vue b/components/galleria/GalleriaContent.vue new file mode 100755 index 000000000..2455bbd71 --- /dev/null +++ b/components/galleria/GalleriaContent.vue @@ -0,0 +1,117 @@ + + + diff --git a/components/galleria/GalleriaItem.vue b/components/galleria/GalleriaItem.vue new file mode 100755 index 000000000..70d3b6d31 --- /dev/null +++ b/components/galleria/GalleriaItem.vue @@ -0,0 +1,160 @@ + + + diff --git a/components/galleria/GalleriaItemSlot.vue b/components/galleria/GalleriaItemSlot.vue new file mode 100755 index 000000000..2057bed24 --- /dev/null +++ b/components/galleria/GalleriaItemSlot.vue @@ -0,0 +1,52 @@ + diff --git a/components/galleria/GalleriaThumbnails.vue b/components/galleria/GalleriaThumbnails.vue new file mode 100755 index 000000000..6bcbc0c36 --- /dev/null +++ b/components/galleria/GalleriaThumbnails.vue @@ -0,0 +1,413 @@ + + + diff --git a/components/galleria/package.json b/components/galleria/package.json new file mode 100644 index 000000000..5b93bf87a --- /dev/null +++ b/components/galleria/package.json @@ -0,0 +1,9 @@ +{ + "main": "./galleria.cjs.js", + "module": "./galleria.esm.js", + "unpkg": "./galleria.min.js", + "types": "./Galleria.d.ts", + "browser": { + "./sfc": "./Galleria.vue" + } +} \ No newline at end of file diff --git a/components/image/Image.d.ts b/components/image/Image.d.ts new file mode 100644 index 000000000..928459bf0 --- /dev/null +++ b/components/image/Image.d.ts @@ -0,0 +1,65 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface ImageProps { + /** + * Controls the preview functionality. + */ + preview?: boolean | undefined; + /** + * Inline style of the image element. + */ + imageStyle?: any; + /** + * Style class of the image element. + */ + imageClass?: any; +} + +export interface ImageSlots { + /** + * Custom indicator template. + */ + indicator: () => VNode[]; +} + +export declare type ImageEmits = { +} + +declare class Image extends ClassComponent { + /** + * Triggered when the preview overlay is shown. + * + * @memberof Image + */ + show: () => void; + /** + * Triggered when the preview overlay is hidden. + * + * @memberof Image + */ + hide: () => void; + /** + * Triggered when an error occurs while loading an image file. + * + * @memberof Image + */ + error: () => void; +} + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Image: GlobalComponentConstructor + } +} + +/** + * + * Displays an image with preview and tranformation options. For multiple image, see Galleria. + * + * Demos: + * + * - [Image](https://www.primefaces.org/primevue/showcase/#/image) + * + */ +export default Image; diff --git a/components/image/Image.spec.js b/components/image/Image.spec.js new file mode 100644 index 000000000..f4fc95b52 --- /dev/null +++ b/components/image/Image.spec.js @@ -0,0 +1,47 @@ +import { mount } from '@vue/test-utils'; +import Image from './Image.vue'; + +describe('Image.vue', () => { + it('should exist', () => { + const wrapper = mount(Image, { + global: { + stubs: { + teleport: true + } + }, + props: { + src: 'demo/images/galleria/galleria1.jpg' + } + }); + + expect(wrapper.find('.p-image.p-component').exists()).toBe(true); + expect(wrapper.find('.p-image.p-component img').attributes().src).toBe('demo/images/galleria/galleria1.jpg'); + }); + + it('should preview', async() => { + const wrapper = mount(Image, { + global: { + stubs: { + teleport: true + } + }, + props: { + src: 'demo/images/galleria/galleria1.jpg', + preview: true + } + }); + + expect(wrapper.find('.p-image-preview-container').exists()).toBe(true); + expect(wrapper.find('.p-image-preview-indicator').exists()).toBe(true); + expect(wrapper.find('.p-image-mask').exists()).toBe(false); + + await wrapper.setData({ maskVisible: true}); + + expect(wrapper.find('.p-image-mask').exists()).toBe(true); + expect(wrapper.find('.p-image-toolbar').exists()).toBe(true); + + await wrapper.setData({ maskVisible: false}); + + expect(wrapper.find('.p-image-mask').exists()).toBe(false); + }); +}); \ No newline at end of file diff --git a/components/image/Image.vue b/components/image/Image.vue new file mode 100644 index 000000000..7e4461838 --- /dev/null +++ b/components/image/Image.vue @@ -0,0 +1,226 @@ + + + + + diff --git a/components/image/package.json b/components/image/package.json new file mode 100644 index 000000000..81643ec36 --- /dev/null +++ b/components/image/package.json @@ -0,0 +1,9 @@ +{ + "main": "./image.cjs.js", + "module": "./image.esm.js", + "unpkg": "./image.min.js", + "types": "./Image.d.ts", + "browser": { + "./sfc": "./Image.vue" + } + } \ No newline at end of file diff --git a/components/inlinemessage/InlineMessage.d.ts b/components/inlinemessage/InlineMessage.d.ts new file mode 100755 index 000000000..bc1020695 --- /dev/null +++ b/components/inlinemessage/InlineMessage.d.ts @@ -0,0 +1,42 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type InlineMessageSeverityType = 'success' | 'info' | 'warn' | 'error' | undefined; + +export interface InlineMessageProps { + /** + * Severity level of the message. + * @see InlineMessageSeverityType + * Default value is 'info'. + */ + severity?: InlineMessageSeverityType; +} + +export interface InlineMessageSlots { + /** + * Default custom slot. + */ + default: () => VNode[]; +} + +export declare type InlineMessageEmits = { +} + +declare class InlineMessage extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + InlineMessage: GlobalComponentConstructor + } +} + +/** + * + * InlineMessage component is useful in cases where a single message needs to be displayed related to an element such as forms. It has one property, severity of the message. + * + * Demos: + * + * - [InlineMessage](https://www.primefaces.org/primevue/showcase/#/message) + * + */ +export default InlineMessage; diff --git a/components/inlinemessage/InlineMessage.spec.js b/components/inlinemessage/InlineMessage.spec.js new file mode 100644 index 000000000..a80e02985 --- /dev/null +++ b/components/inlinemessage/InlineMessage.spec.js @@ -0,0 +1,19 @@ +import { mount } from '@vue/test-utils'; +import InlineMessage from './InlineMessage.vue'; + +describe('InlineMessage.vue', () => { + it('should exist', () => { + const wrapper = mount(InlineMessage, { + slots: { + default: 'Username is required' + }, + props: { + severity: 'error', + } + }); + + expect(wrapper.find('.p-inline-message.p-component').exists()).toBe(true); + expect(wrapper.find('.p-inline-message.p-component').text()).toBe('Username is required'); + expect(wrapper.find('.p-inline-message-error').exists()).toBe(true); + }); +}); \ No newline at end of file diff --git a/components/inlinemessage/InlineMessage.vue b/components/inlinemessage/InlineMessage.vue new file mode 100755 index 000000000..fcd70560f --- /dev/null +++ b/components/inlinemessage/InlineMessage.vue @@ -0,0 +1,62 @@ + + + + + diff --git a/components/inlinemessage/package.json b/components/inlinemessage/package.json new file mode 100644 index 000000000..fb4463886 --- /dev/null +++ b/components/inlinemessage/package.json @@ -0,0 +1,9 @@ +{ + "main": "./inlinemessage.cjs.js", + "module": "./inlinemessage.esm.js", + "unpkg": "./inlinemessage.min.js", + "types": "./InlineMessage.d.ts", + "browser": { + "./sfc": "./InlineMessage.vue" + } +} \ No newline at end of file diff --git a/components/inplace/Inplace.d.ts b/components/inplace/Inplace.d.ts new file mode 100755 index 000000000..f04f4cc04 --- /dev/null +++ b/components/inplace/Inplace.d.ts @@ -0,0 +1,65 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface InplaceProps { + /** + * Displays a button to switch back to display mode. + */ + closable?: boolean | undefined; + /** + * Whether the content is displayed or not. + */ + active?: boolean | undefined; + /** + * When present, it specifies that the element should be disabled. + */ + disabled?: boolean | undefined; +} + +export interface InplaceSlots { + /** + * Custom display template. + */ + display: () => VNode[]; + /** + * Custom content template. + */ + content: () => VNode[]; +} + +export declare type InplaceEmits = { + /** + * Emitted when the active changes. + * @param {boolean} value - New value. + */ + 'update:active': (value: boolean) => void; + /** + * Callback to invoke when inplace is opened. + * @param {Event} event - Browser event. + */ + 'open': (event: Event) => void; + /** + * Callback to invoke when inplace is closed. + * @param {Event} event - Browser event. + */ + 'close': (event: Event) => void; +} + +declare class Inplace extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Inplace: GlobalComponentConstructor + } +} + +/** + * + * Inplace provides an easy to do editing and display at the same time where clicking the output displays the actual content. + * + * Demos: + * + * - [Inplace](https://www.primefaces.org/primevue/showcase/#/inplace) + * + */ +export default Inplace; diff --git a/components/inplace/Inplace.spec.js b/components/inplace/Inplace.spec.js new file mode 100644 index 000000000..c79642ef1 --- /dev/null +++ b/components/inplace/Inplace.spec.js @@ -0,0 +1,67 @@ +import { mount } from '@vue/test-utils'; +import Inplace from './Inplace.vue'; +import InputText from '@/components/inputtext/InputText.vue'; + +describe('Inplace.vue', () => { + it('should exist', () => { + const wrapper = mount(Inplace); + + expect(wrapper.find('.p-inplace.p-component').exists()).toBe(true); + }); + + it('should slots display', () => { + const wrapper = mount(Inplace, { + global: { + components: { + InputText + } + }, + slots: { + display: ` + + View Picture + `, + content: `` + } + }); + + expect(wrapper.find('.p-inplace-display').exists()).toBe(true); + + wrapper.vm.open({}); + + expect(wrapper.emitted()['update:active'][0]).toEqual([true]); + + wrapper.vm.close({}); + + expect(wrapper.emitted()['update:active'][1]).toEqual([false]); + }); + + it('closable inplace', async() => { + const wrapper = mount(Inplace, { + global: { + components: { + InputText + } + }, + props: { + closable: true + }, + slots: { + display: `{{'Click to Edit'}}`, + content: `` + } + }); + + expect(wrapper.find('.p-inplace-closable').exists()).toBe(true); + expect(wrapper.find('.p-inplace-display').text()).toBe('Click to Edit'); + + await wrapper.vm.open({}); + + expect(wrapper.find('.p-inputtext').exists()).toBe(true); + expect(wrapper.find('.pi.pi-times').exists()).toBe(true); + + await wrapper.vm.close({}); + + expect(wrapper.find('.pi.pi-times').exists()).toBe(false); + }); +}); \ No newline at end of file diff --git a/components/inplace/Inplace.vue b/components/inplace/Inplace.vue new file mode 100755 index 000000000..adbcafaff --- /dev/null +++ b/components/inplace/Inplace.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/components/inplace/package.json b/components/inplace/package.json new file mode 100644 index 000000000..e64e208c1 --- /dev/null +++ b/components/inplace/package.json @@ -0,0 +1,9 @@ +{ + "main": "./inplace.cjs.js", + "module": "./inplace.esm.js", + "unpkg": "./inplace.min.js", + "types": "./Inplace.d.ts", + "browser": { + "./sfc": "./Inplace.vue" + } +} \ No newline at end of file diff --git a/components/inputmask/InputMask.d.ts b/components/inputmask/InputMask.d.ts new file mode 100755 index 000000000..dad03a146 --- /dev/null +++ b/components/inputmask/InputMask.d.ts @@ -0,0 +1,80 @@ +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface InputMaskProps { + /** + * Value of the component. + */ + modelValue?: string | undefined; + /** + * Placeholder character in mask, default is underscore. + * Default value is '_'. + */ + slotChar?: string | undefined; + /** + * Mask pattern. + */ + mask?: string | undefined; + /** + * Clears the incomplete value on blur. + * Default value is true. + */ + autoClear?: boolean | undefined; + /** + * Defines if model sets the raw unmasked value to bound value or the formatted mask value. + */ + unmask?: boolean | undefined; +} + +export interface InputMaskSlots { +} + +export declare type InputMaskEmits = { + /** + * Emitted when the value changes. + * @param {string} value - New value. + */ + 'update:modelValue': (value: string) => void; + /** + * Callback to invoke when the component receives focus. + */ + 'focus': (event: Event) => void; + /** + * Callback to invoke when the component loses focus. + */ + 'blur': (event: Event) => void; + /** + * Callback to invoke when a key is pressed. + */ + 'keydown': (event: Event) => void; + /** + * Callback to invoke when a key that produces a character value is pressed down. + */ + 'keypress': (event: Event) => void; + /** + * Callback to invoke when the user has initiated a 'paste' action through the browser's user interface. + */ + 'paste': (event: Event) => void; + /** + * Callback to invoke when the mask is completed. + */ + 'complete': (event: Event) => void; +} + +declare class InputMask extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + InputMask: GlobalComponentConstructor + } +} + +/** + * + * InputMask component is used to enter input in a certain format such as numeric, date, currency, email and phone. + * + * Demos: + * + * - [InputMask](https://www.primefaces.org/primevue/showcase/#/inputmask) + * + */ +export default InputMask; diff --git a/components/inputmask/InputMask.spec.js b/components/inputmask/InputMask.spec.js new file mode 100644 index 000000000..9ff06f86b --- /dev/null +++ b/components/inputmask/InputMask.spec.js @@ -0,0 +1,38 @@ +import { mount } from '@vue/test-utils'; +import InputMask from './InputMask.vue'; + +describe('InputMask.vue', () => { + it('should exist', async () => { + const wrapper = mount(InputMask, { + props: { + modelValue: null, + mask: '99-999999', + placeholder: '99-999999' + } + }); + + expect(wrapper.find('.p-inputmask.p-component').exists()).toBe(true); + expect(wrapper.find('.p-inputmask.p-component').attributes().placeholder).toBe('99-999999'); + + const event = {'target': { 'value': '1' }}; + + await wrapper.vm.onInput(event); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual(['1']); + }); + + it('keydown event', async () => { + const wrapper = mount(InputMask, { + props: { + modelValue: null, + mask: '99/99/9999' + } + }); + + const event = {'target': { 'value': '1' }}; + + await wrapper.vm.onKeyDown(event); + + expect(wrapper.emitted().keydown[0]).toEqual([event]); + }); +}); \ No newline at end of file diff --git a/components/inputmask/InputMask.vue b/components/inputmask/InputMask.vue new file mode 100755 index 000000000..5ce0201fc --- /dev/null +++ b/components/inputmask/InputMask.vue @@ -0,0 +1,494 @@ + + + diff --git a/components/inputmask/package.json b/components/inputmask/package.json new file mode 100644 index 000000000..024546eb7 --- /dev/null +++ b/components/inputmask/package.json @@ -0,0 +1,9 @@ +{ + "main": "./inputmask.cjs.js", + "module": "./inputmask.esm.js", + "unpkg": "./inputmask.min.js", + "types": "./InputMask.d.ts", + "browser": { + "./sfc": "./InputMask.vue" + } +} \ No newline at end of file diff --git a/components/inputnumber/InputNumber.d.ts b/components/inputnumber/InputNumber.d.ts new file mode 100755 index 000000000..fb9887266 --- /dev/null +++ b/components/inputnumber/InputNumber.d.ts @@ -0,0 +1,237 @@ +import { ButtonHTMLAttributes, InputHTMLAttributes } from 'vue'; +import { ClassComponent, GlobalComponentConstructor, Nullable } from '../ts-helpers'; + +type InputNumberButtonLayoutType = 'stacked' | 'horizontal' | 'vertical' | undefined; + +type InputNumberLocaleMatcherType = 'lookup' | 'best fit' | undefined; + +type InputNumberModeType = 'decimal' | 'currency' | undefined; + +export interface InputNumberInputEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * New value + */ + value: string | number | undefined; +} + +export interface InputNumberBlurEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Input value + */ + value: string; +} + +export interface InputNumberProps { + /** + * Value of the component. + */ + modelValue?: Nullable; + /** + * Whether to format the value. + */ + format?: boolean | undefined; + /** + * Displays spinner buttons. + */ + showButtons?: boolean | undefined; + /** + * Layout of the buttons. + * @see InputNumberButtonLayoutType + * Default value is 'stacked'. + */ + buttonLayout?: InputNumberButtonLayoutType; + /** + * Style class of the increment button. + */ + incrementButtonClass?: string | undefined; + /** + * Style class of the decrement button. + */ + decrementButtonClass?: string | undefined; + /** + * Style class of the increment button. + * Default value is 'pi pi-angle-up'. + */ + incrementButtonIcon?: string | undefined; + /** + * Style class of the decrement button. + * Default value is 'pi pi-angle-down'. + */ + decrementButtonIcon?: string | undefined; + /** + * Locale to be used in formatting. + */ + locale?: string | undefined; + /** + * The locale matching algorithm to use. Possible values are 'lookup' and 'best fit'; the default is 'best fit'. + * See [Locale Negotation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#locale_negotiation) for details. + * @see InputNumberLocaleMatcherType + * Default value is 'best fit' + */ + localeMatcher?: InputNumberLocaleMatcherType; + /** + * Defines the behavior of the component. + * @see InputNumberModeType + * Default value is 'decimal'. + */ + mode?: InputNumberModeType; + /** + * Text to display before the value. + */ + prefix?: string | undefined; + /** + * Text to display after the value. + */ + suffix?: string | undefined; + /** + * The currency to use in currency formatting. Possible values are the [ISO 4217 currency codes](https://www.six-group.com/en/products-services/financial-information/data-standards.html#scrollTo=maintenance-agency), such as 'USD' for the US dollar, 'EUR' for the euro, or 'CNY' for the Chinese RMB. + * There is no default value; if the style is 'currency', the currency property must be provided. + */ + currency?: string | undefined; + /** + * How to display the currency in currency formatting. Possible values are 'symbol' to use a localized currency symbol such as €, 'code' to use the ISO currency code, 'name' to use a localized currency name such as 'dollar'. + * Default value is 'symbol'. + */ + currencyDisplay?: string | undefined; + /** + * Whether to use grouping separators, such as thousands separators or thousand/lakh/crore separators. + * Default value is true. + */ + useGrouping?: boolean | undefined; + /** + * The minimum number of fraction digits to use. Possible values are from 0 to 20; the default for plain number and percent formatting is 0; + * the default for currency formatting is the number of minor unit digits provided by the [ISO 4217 currency code](https://www.six-group.com/en/products-services/financial-information/data-standards.html#scrollTo=maintenance-agency) list (2 if the list doesn't provide that information). + */ + minFractionDigits?: number | undefined; + /** + * The maximum number of fraction digits to use. Possible values are from 0 to 20; the default for plain number formatting is the larger of minimumFractionDigits and 3; + * the default for currency formatting is the larger of minimumFractionDigits and the number of minor unit digits provided by the [ISO 4217 currency code](https://www.six-group.com/en/products-services/financial-information/data-standards.html#scrollTo=maintenance-agency) list (2 if the list doesn't provide that information). + */ + maxFractionDigits?: number | undefined; + /** + * Mininum boundary value. + */ + min?: number | undefined; + /** + * Maximum boundary value. + */ + max?: number | undefined; + /** + * Step factor to increment/decrement the value. + * Default value is 1. + */ + step?: number | undefined; + /** + * Determines whether the input field is empty. + * Default value is true. + */ + allowEmpty?: boolean | undefined; + /** + * When present, it specifies that the component should be disabled. + */ + disabled?: boolean | undefined; + /** + * When present, it specifies that an input field is read-only. + */ + readonly?: boolean | undefined; + /** + * Placeholder text for the input. + */ + placeholder?: string | undefined; + /** + * Identifier of the focus input to match a label defined for the chips. + */ + inputId?: string | undefined; + /** + * Style class of the input field. + */ + inputClass?: any | undefined; + /** + * Inline style of the input field. + */ + inputStyle?: any | undefined; + /** + * Uses to pass all properties of the HTMLInputElement to the focusable input element inside the component. + */ + inputProps?: InputHTMLAttributes | undefined; + /** + * Uses to pass all properties of the HTMLButtonElement to increment button inside the component. + */ + incrementButtonProps?: ButtonHTMLAttributes | undefined; + /** + * Uses to pass all properties of the HTMLButtonElement to decrement button inside the component. + */ + decrementButtonProps?: ButtonHTMLAttributes | undefined; + /** + * Establishes relationships between the component and label(s) where its value should be one or more element IDs. + */ + 'aria-labelledby'?: string | undefined; + /** + * Establishes a string value that labels the component. + */ + 'aria-label'?: string | undefined; +} + +export interface InputNumberSlots { +} + +export declare type InputNumberEmits = { + /** + * Emitted when the value changes. + * @param {number} value - New value. + */ + 'update:modelValue': (value: number) => void; + /** + * Callback to invoke when the value is entered. + * @param {InputNumberInputEvent} event - Custom input event. + */ + 'input': (event: InputNumberInputEvent) => void; + /** + * Callback to invoke on focus of input field. + * @param {Event} event - Focus event + */ + 'focus': (event: Event) => void; + /** + * Callback to invoke on blur of input field. + * @param {InputNumberBlurEvent} event - Blur event + */ + 'blur': (event: InputNumberBlurEvent) => void; +} + +declare class InputNumber extends ClassComponent { + /** + * Returns Intl.NumberFormat object. + * + * @memberof InputNumber + */ + getFormatter: () => Intl.NumberFormat | undefined; +} + +declare module '@vue/runtime-core' { + interface GlobalComponents { + InputNumber: GlobalComponentConstructor + } +} + +/** + * + * InputNumber is an input component to provide numerical input. + * + * Helper API: + * + * - [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat) + * + * Demos: + * + * - [InputNumber](https://www.primefaces.org/primevue/showcase/#/inputnumber) + * + */ +export default InputNumber; diff --git a/components/inputnumber/InputNumber.spec.js b/components/inputnumber/InputNumber.spec.js new file mode 100644 index 000000000..9d735de65 --- /dev/null +++ b/components/inputnumber/InputNumber.spec.js @@ -0,0 +1,95 @@ +import { mount } from '@vue/test-utils'; +import InputNumber from './InputNumber.vue'; + +describe('InputNumber.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(InputNumber, { + props: { + modelValue: 1 + } + }); + }); + + it('is exist', () => { + expect(wrapper.find('.p-inputnumber.p-component').exists()).toBe(true); + expect(wrapper.find('input.p-inputnumber-input').exists()).toBe(true); + }); + + it('is keydown called when down and up keys pressed', async () => { + await wrapper.vm.onInputKeyDown({which: 38, target: { value: 1 }, preventDefault: () => {}}); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual([2]); + + await wrapper.vm.onInputKeyDown({which: 40, target: { value: 2 }, preventDefault: () => {}}) + + expect(wrapper.emitted()['update:modelValue'][1]).toEqual([1]); + }); + + it('is keydown called when tab key pressed', async () => { + await wrapper.vm.onInputKeyDown({which: 9, target: { value: '12' }, preventDefault: () => {}}); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual([12]); + expect(wrapper.find('input.p-inputnumber-input').attributes()['aria-valuenow']).toBe('12'); + }); + + it('is keydown called when enter key pressed', async () => { + await wrapper.vm.onInputKeyDown({which: 13, target: { value: '12' }, preventDefault: () => {}}); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual([12]); + expect(wrapper.find('input.p-inputnumber-input').attributes()['aria-valuenow']).toBe('12'); + }); + + it('is keypress called when pressed a number', async () => { + wrapper.find('input.p-inputnumber-input').element.setSelectionRange(2,2); + + await wrapper.vm.onInputKeyPress({which: 49, preventDefault: () => {}}); + + expect(wrapper.emitted().input[0][0].value).toBe(11); + }); + + it('is keypress called when pressed minus', async () => { + wrapper.find('input.p-inputnumber-input').element.setSelectionRange(0,0); + + await wrapper.vm.onInputKeyPress({keyCode: 45, preventDefault: () => {}}); + + expect(wrapper.emitted().input[0][0].value).toBe(-1); + }); + + it('should have min boundary', async () => { + await wrapper.setProps({ modelValue: 95, min: 95}); + + await wrapper.vm.onInputKeyDown({which: 40, target: { value: 96 }, preventDefault: () => {}}); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual([95]); + + await wrapper.vm.onInputKeyDown({which: 40, target: { value: 95 }, preventDefault: () => {}}); + + expect(wrapper.emitted()['update:modelValue'][1]).toEqual([95]); + }); + + it('should have max boundary', async () => { + await wrapper.setProps({ modelValue: 99, max: 100}); + + await wrapper.vm.onInputKeyDown({which: 38, target: { value: 99 }, preventDefault: () => {}}); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual([100]); + + await wrapper.vm.onInputKeyDown({which: 38, target: { value: 100 }, preventDefault: () => {}}); + + expect(wrapper.emitted()['update:modelValue'][1]).toEqual([100]); + }); + + it('should have currency', async () => { + await wrapper.setProps({ modelValue: 12345, mode: 'currency', currency: 'USD', locale: 'en-US'}); + + expect(wrapper.find('input.p-inputnumber-input').element._value).toBe('$12,345.00'); + }); + + it('should have prefix', async () => { + await wrapper.setProps({ modelValue: 20, prefix: '%' }); + + expect(wrapper.find('input.p-inputnumber-input').element._value).toBe('%20'); + }); +}); \ No newline at end of file diff --git a/components/inputnumber/InputNumber.vue b/components/inputnumber/InputNumber.vue new file mode 100755 index 000000000..c9d26e7f0 --- /dev/null +++ b/components/inputnumber/InputNumber.vue @@ -0,0 +1,1139 @@ + + + + + diff --git a/components/inputnumber/package.json b/components/inputnumber/package.json new file mode 100644 index 000000000..fa7652483 --- /dev/null +++ b/components/inputnumber/package.json @@ -0,0 +1,9 @@ +{ + "main": "./inputnumber.cjs.js", + "module": "./inputnumber.esm.js", + "unpkg": "./inputnumber.min.js", + "types": "./InputNumber.d.ts", + "browser": { + "./sfc": "./InputNumber.vue" + } +} \ No newline at end of file diff --git a/components/inputswitch/InputSwitch.d.ts b/components/inputswitch/InputSwitch.d.ts new file mode 100755 index 000000000..86c718c56 --- /dev/null +++ b/components/inputswitch/InputSwitch.d.ts @@ -0,0 +1,86 @@ +import { InputHTMLAttributes } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface InputSwitchProps { + /** + * Specifies whether a inputswitch should be checked or not. + */ + modelValue?: boolean | string | undefined; + /** + * Value in checked state. + */ + trueValue?: any; + /** + * Value in unchecked state. + */ + falseValue?: any; + /** + * Identifier of the underlying input element. + */ + inputId?: string | undefined; + /** + * Style class of the input field. + */ + inputClass?: any | undefined; + /** + * Inline style of the input field. + */ + inputStyle?: any | undefined; + /** + * Uses to pass all properties of the HTMLInputElement to the focusable input element inside the component. + */ + inputProps?: InputHTMLAttributes | undefined; + /** + * Establishes relationships between the component and label(s) where its value should be one or more element IDs. + */ + 'aria-labelledby'?: string | undefined; + /** + * Establishes a string value that labels the component. + */ + 'aria-label'?: string | undefined; +} + +export interface InputSwitchSlots { +} + +export declare type InputSwitchEmits = { + /** + * Emitted when the value changes. + * @param {boolean} value - New value. + */ + 'update:modelValue': (value: boolean) => void; + /** + * Callback to invoke on click. + * @param {Event} event - Browser event. + */ + 'click': (event: Event) => void; + /** + * Callback to invoke on value change. + * @param {Event} event - Browser event. + */ + 'change': (event: Event) => void; + /** + * Callback to invoke on value change. + * @param {boolean} value - New value. + */ + 'input': (value: boolean) => void; +} + +declare class InputSwitch extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + InputSwitch: GlobalComponentConstructor + } +} + +/** + * + * InputSwitch is used to select a boolean value. + * + * Demos: + * + * - [InputSwitch](https://www.primefaces.org/primevue/showcase/#/inputswitch) + * + */ +export default InputSwitch; diff --git a/components/inputswitch/InputSwitch.spec.js b/components/inputswitch/InputSwitch.spec.js new file mode 100644 index 000000000..20a160933 --- /dev/null +++ b/components/inputswitch/InputSwitch.spec.js @@ -0,0 +1,20 @@ +import { mount } from '@vue/test-utils'; +import InputSwitch from './InputSwitch.vue'; + +describe('InputSwitch.vue', () => { + it('should exist', async () => { + const wrapper = mount(InputSwitch); + + expect(wrapper.find('.p-inputswitch.p-component').exists()).toBe(true); + expect(wrapper.find('.p-inputswitch-slider').exists()).toBe(true); + + await wrapper.trigger('click'); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual([true]); + + await wrapper.setProps({ modelValue: true }); + + expect(wrapper.vm.checked).toBe(true); + expect(wrapper.find('.p-inputswitch').classes()).toContain('p-inputswitch-checked'); + }); +}); \ No newline at end of file diff --git a/components/inputswitch/InputSwitch.vue b/components/inputswitch/InputSwitch.vue new file mode 100755 index 000000000..9a1d2852e --- /dev/null +++ b/components/inputswitch/InputSwitch.vue @@ -0,0 +1,109 @@ + + + + + diff --git a/components/inputswitch/package.json b/components/inputswitch/package.json new file mode 100644 index 000000000..09fcb7004 --- /dev/null +++ b/components/inputswitch/package.json @@ -0,0 +1,9 @@ +{ + "main": "./inputswitch.cjs.js", + "module": "./inputswitch.esm.js", + "unpkg": "./inputswitch.min.js", + "types": "./InputSwitch.d.ts", + "browser": { + "./sfc": "./InputSwitch.vue" + } +} \ No newline at end of file diff --git a/components/inputtext/InputText.css b/components/inputtext/InputText.css new file mode 100755 index 000000000..ce006b918 --- /dev/null +++ b/components/inputtext/InputText.css @@ -0,0 +1,88 @@ +.p-inputtext { + margin: 0; +} + +.p-fluid .p-inputtext { + width: 100%; +} + +/* InputGroup */ +.p-inputgroup { + display: flex; + align-items: stretch; + width: 100%; +} + +.p-inputgroup-addon { + display: flex; + align-items: center; + justify-content: center; +} + +.p-inputgroup .p-float-label { + display: flex; + align-items: stretch; + width: 100%; +} + +.p-inputgroup .p-inputtext, +.p-fluid .p-inputgroup .p-inputtext, +.p-inputgroup .p-inputwrapper, +.p-fluid .p-inputgroup .p-input { + flex: 1 1 auto; + width: 1%; +} + +/* Floating Label */ +.p-float-label { + display: block; + position: relative; +} + +.p-float-label label { + position: absolute; + pointer-events: none; + top: 50%; + margin-top: -.5rem; + transition-property: all; + transition-timing-function: ease; + line-height: 1; +} + +.p-float-label textarea ~ label { + top: 1rem; +} + +.p-float-label input:focus ~ label, +.p-float-label input.p-filled ~ label, +.p-float-label textarea:focus ~ label, +.p-float-label textarea.p-filled ~ label, +.p-float-label .p-inputwrapper-focus ~ label, +.p-float-label .p-inputwrapper-filled ~ label { + top: -.75rem; + font-size: 12px; +} + +.p-float-label .input:-webkit-autofill ~ label { + top: -20px; + font-size: 12px; +} + +.p-input-icon-left, +.p-input-icon-right { + position: relative; + display: inline-block; +} + +.p-input-icon-left > i, +.p-input-icon-right > i { + position: absolute; + top: 50%; + margin-top: -.5rem; +} + +.p-fluid .p-input-icon-left, +.p-fluid .p-input-icon-right { + display: block; + width: 100%; +} \ No newline at end of file diff --git a/components/inputtext/InputText.d.ts b/components/inputtext/InputText.d.ts new file mode 100755 index 000000000..afe880f98 --- /dev/null +++ b/components/inputtext/InputText.d.ts @@ -0,0 +1,39 @@ +import { InputHTMLAttributes } from 'vue'; +import { ClassComponent, GlobalComponentConstructor, Nullable } from '../ts-helpers'; + +export interface InputTextProps extends InputHTMLAttributes { + /** + * Value of the component. + */ + modelValue?: Nullable; +} + +export interface InputTextSlots { +} + +export declare type InputTextEmits = { + /** + * Emitted when the value changes. + * @param {string} value - New value. + */ + 'update:modelValue': (value: string | undefined) => void; +} + +declare class InputText extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + InputText: GlobalComponentConstructor + } +} + +/** + * + * InputText renders a text field to enter data. + * + * Demos: + * + * - [InputText](https://www.primefaces.org/primevue/showcase/#/inputtext) + * + */ +export default InputText; diff --git a/components/inputtext/InputText.spec.js b/components/inputtext/InputText.spec.js new file mode 100644 index 000000000..671599671 --- /dev/null +++ b/components/inputtext/InputText.spec.js @@ -0,0 +1,34 @@ +import { mount } from '@vue/test-utils'; +import InputText from './InputText.vue'; + +describe('InputText.vue', () => { + it('is InputText component exists', async () => { + const wrapper = mount(InputText); + + expect(wrapper.find('.p-inputtext.p-component').exists()).toBe(true); + + await wrapper.setProps({ modelValue: 'PrimeVue' }); + + expect(wrapper.find('.p-filled').exists()).toBe(true); + + const input = wrapper.find('input'); + expect(input.element.value).toEqual('PrimeVue'); + }); + + it('input event', async () => { + const wrapper = mount(InputText); + const event = { target: { value: 'a' } }; + + await wrapper.vm.onInput(event); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual(['a']); + }); + + it('should filled work', async () => { + const wrapper = mount(InputText); + + await wrapper.setProps({ modelValue: 'a' }); + + expect(wrapper.vm.filled).toBe(true); + }); +}); diff --git a/components/inputtext/InputText.vue b/components/inputtext/InputText.vue new file mode 100755 index 000000000..de67a8225 --- /dev/null +++ b/components/inputtext/InputText.vue @@ -0,0 +1,23 @@ + + + diff --git a/components/inputtext/package.json b/components/inputtext/package.json new file mode 100644 index 000000000..43d4e2f56 --- /dev/null +++ b/components/inputtext/package.json @@ -0,0 +1,9 @@ +{ + "main": "./inputtext.cjs.js", + "module": "./inputtext.esm.js", + "unpkg": "./inputtext.min.js", + "types": "./InputText.d.ts", + "browser": { + "./sfc": "./InputText.vue" + } +} \ No newline at end of file diff --git a/components/knob/Knob.d.ts b/components/knob/Knob.d.ts new file mode 100644 index 000000000..0ac52cad8 --- /dev/null +++ b/components/knob/Knob.d.ts @@ -0,0 +1,109 @@ +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface KnobProps { + /** + * Value of the component. + */ + modelValue?: number | undefined; + /** + * Size of the component in pixels. + * Default value is 100. + */ + size?: number | undefined; + /** + * When present, it specifies that the component should be disabled. + */ + disabled?: boolean | undefined; + /** + * When present, it specifies that the component value cannot be edited. + */ + readonly?: boolean | undefined; + /** + * Step factor to increment/decrement the value. + */ + step?: number | undefined; + /** + * Mininum boundary value. + * Default value is 0. + */ + min?: number | undefined; + /** + * Maximum boundary value. + * Default value is 100. + */ + max?: number | undefined; + /** + * Background of the value. + */ + valueColor?: string | undefined; + /** + * Background color of the range. + */ + rangeColor?: string | undefined; + /** + * Color of the value text. + */ + textColor?: string | undefined; + /** + * Width of the knob stroke. + * Default value is 14. + */ + strokeWidth?: number | undefined; + /** + * Whether the show the value inside the knob. + * Default value is true. + */ + showValue?: boolean | undefined; + /** + * Template string of the value. + * Default value is '{value}'. + */ + valueTemplate?: string | undefined; + /** + * Index of the element in tabbing order. + */ + tabindex?: number | undefined; + /** + * Establishes relationships between the component and label(s) where its value should be one or more element IDs. + */ + 'aria-labelledby'?: string | undefined; + /** + * Used to define a string that labels the element. + */ + 'aria-label'?: string | undefined; +} + +export interface KnobSlots { +} + +export declare type KnobEmits = { + /** + * Emitted when the value changes. + * @param {number} value - New value. + */ + 'update:modelValue': (value: number) => void; + /** + * Callback to invoke when the value changes. + * @param {number} value - New value + */ + 'change': (value: number) => void; +} + +declare class Knob extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Knob: GlobalComponentConstructor + } +} + +/** + * + * Knob is a form component to define number inputs with a dial. + * + * Demos: + * + * - [Knob](https://www.primefaces.org/primevue/showcase/#/knob) + * + */ +export default Knob; diff --git a/components/knob/Knob.spec.js b/components/knob/Knob.spec.js new file mode 100644 index 000000000..f99622b1f --- /dev/null +++ b/components/knob/Knob.spec.js @@ -0,0 +1,47 @@ +import { mount } from '@vue/test-utils'; +import Knob from './Knob.vue'; + +describe('Knob.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Knob, { + props: { + modelValue: 20 + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-knob.p-component').exists()).toBe(true); + expect(wrapper.find('.p-knob-text').text()).toBe('20'); + }); + + it('should change with click event', async () => { + const event = { offsetX: 100, offsetY: 100 }; + + await wrapper.vm.onClick(event); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual([95]); + }); + + it('should min - max work', async () => { + await wrapper.setProps({ min: -50, max: 50 }); + + const event = { offsetX: 100, offsetY: 100 }; + + await wrapper.vm.onClick(event); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual([45]); + }); + + it('should step work', async () => { + await wrapper.setProps({ step: 10 }); + + const event = { offsetX: 18, offsetY: 30 }; + + await wrapper.vm.onClick(event); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual([30]); + }); +}); \ No newline at end of file diff --git a/components/knob/Knob.vue b/components/knob/Knob.vue new file mode 100644 index 000000000..628769771 --- /dev/null +++ b/components/knob/Knob.vue @@ -0,0 +1,292 @@ + + + + + diff --git a/components/knob/package.json b/components/knob/package.json new file mode 100644 index 000000000..1f1bab14b --- /dev/null +++ b/components/knob/package.json @@ -0,0 +1,9 @@ +{ + "main": "./knob.cjs.js", + "module": "./knob.esm.js", + "unpkg": "./knob.min.js", + "types": "./Knob.d.ts", + "browser": { + "./sfc": "./Knob.vue" + } +} \ No newline at end of file diff --git a/components/listbox/Listbox.d.ts b/components/listbox/Listbox.d.ts new file mode 100755 index 000000000..1721ab87b --- /dev/null +++ b/components/listbox/Listbox.d.ts @@ -0,0 +1,311 @@ +import { InputHTMLAttributes, VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; +import { VirtualScrollerProps, VirtualScrollerItemOptions } from '../virtualscroller'; + + +type ListboxOptionLabelType = string | ((data: any) => string) | undefined; + +type ListboxOptionValueType = string | ((data: any) => any) | undefined; + +type ListboxOptionDisabledType = string | ((data: any) => boolean) | undefined; + +type ListboxOptionChildrenType = string | ((data: any) => any[]) | undefined; + +type ListboxFilterMatchModeType = 'contains' | 'startsWith' | 'endsWith' | undefined; + +export interface ListboxChangeEvent { + /** + * Original event + */ + originalEvent: Event; + /** + * Selected option value + */ + value: any; +} + +export interface ListboxFilterEvent { + /** + * Original event + */ + originalEvent: Event; + /** + * Filter value + */ + value: string; +} + +export interface ListboxProps { + /** + * Value of the component. + */ + modelValue?: any; + /** + * An array of selectitems to display as the available options. + */ + options?: any[] | undefined; + /** + * Property name or getter function to use as the label of an option. + */ + optionLabel?: ListboxOptionLabelType; + /** + * Property name or getter function to use as the value of an option, defaults to the option itself when not defined. + */ + optionValue?: ListboxOptionValueType; + /** + * Property name or getter function to use as the disabled flag of an option, defaults to false when not defined. + */ + optionDisabled?: ListboxOptionDisabledType; + /** + * Property name or getter function to use as the label of an option group. + */ + optionGroupLabel?: ListboxOptionLabelType; + /** + * Property name or getter function that refers to the children options of option group. + */ + optionGroupChildren?: ListboxOptionChildrenType; + /** + * Inline style of inner list element. + */ + listStyle?: string | undefined; + /** + * When specified, disables the component. + */ + disabled?: boolean | undefined; + /** + * A property to uniquely identify an option. + */ + dataKey?: string | undefined; + /** + * When specified, allows selecting multiple values. + */ + multiple?: boolean | undefined; + /** + * Defines how multiple items can be selected, when true metaKey needs to be pressed to select or unselect an item and when set to false selection of each item can be toggled individually. + * On touch enabled devices, metaKeySelection is turned off automatically. + * Default value is true. + */ + metaKeySelection?: boolean | undefined; + /** + * When specified, displays a filter input at header. + */ + filter?: boolean | undefined; + /** + * Placeholder text to show when filter input is empty. + */ + filterPlaceholder?: string | undefined; + /** + * Locale to use in filtering. The default locale is the host environment's current locale. + */ + filterLocale?: string | undefined; + /** + * Defines the filtering algorithm to use when searching the options. + * @see ListboxFilterMatchModeType + * Default value is 'contains'. + */ + filterMatchMode?: ListboxFilterMatchModeType; + /** + * Fields used when filtering the options, defaults to optionLabel. + */ + filterFields?: string[] | undefined; + /** + * Uses to pass all properties of the HTMLInputElement to the filter input inside the component. + */ + filterInputProps?: InputHTMLAttributes | undefined; + /** + * Whether to use the virtualScroller feature. The properties of VirtualScroller component can be used like an object in it. + * @see VirtualScroller.VirtualScrollerProps + */ + virtualScrollerOptions?: VirtualScrollerProps; + /** + * Whether to focus on the first visible or selected element. + * Default value is true. + */ + autoOptionFocus?: boolean | undefined; + /** + * Text to be displayed in hidden accessible field when filtering returns any results. Defaults to value from PrimeVue locale configuration. + * Default value is '{0} results are available'. + */ + filterMessage?: string | undefined; + /** + * Text to be displayed in hidden accessible field when options are selected. Defaults to value from PrimeVue locale configuration. + * Default value is '{0} items selected'. + */ + selectionMessage?: string | undefined; + /** + * Text to be displayed in hidden accessible field when any option is not selected. Defaults to value from PrimeVue locale configuration. + * Default value is 'No selected item'. + */ + emptySelectionMessage?: string | undefined; + /** + * Text to display when filtering does not return any results. Defaults to value from PrimeVue locale configuration. + * Default value is 'No results found'. + */ + emptyFilterMessage?: string | undefined; + /** + * Text to display when there are no options available. Defaults to value from PrimeVue locale configuration. + * Default value is 'No results found'. + */ + emptyMessage?: string | undefined; + /** + * Index of the element in tabbing order. + */ + tabindex?: number | string | undefined; + /** + * Defines a string value that labels an interactive element. + */ + "aria-label"?: string | undefined; + /** + * Identifier of the underlying input element. + */ + "aria-labelledby"?: string | undefined; +} + +export interface ListboxSlots { + /** + * Custom header template. + * @param {Object} scope - header slot's params. + */ + header: (scope: { + /** + * Value of the component + */ + value: any; + /** + * Displayed options + */ + options: any[]; + }) => VNode[]; + /** + * Custom footer template. + * @param {Object} scope - footer slot's params. + */ + footer: (scope: { + /** + * Value of the component + */ + value: any; + /** + * Displayed options + */ + options: any[]; + }) => VNode[]; + /** + * Custom option template. + * @param {Object} scope - option slot's params. + */ + option: (scope: { + /** + * Option instance + */ + option: any; + /** + * Index of the option + */ + index: number; + }) => VNode[]; + /** + * Custom optiongroup template. + * @param {Object} scope - optiongroup slot's params. + */ + optiongroup: (scope: { + /** + * Option instance + */ + option: any; + /** + * Index of the option + */ + index: number; + }) => VNode[]; + /** + * Custom emptyfilter template. + */ + emptyfilter: () => VNode[]; + /** + * Custom empty template. + */ + empty: () => VNode[]; + /** + * Custom content template. + * @param {Object} scope - content slot's params. + */ + content: (scope: { + /** + * An array of objects to display for virtualscroller + */ + items: any; + /** + * Style class of the component + */ + styleClass: string; + /** + * Referance of the content + * @param {HTMLElement} el - Element of 'ref' property + */ + contentRef(el: any): void; + /** + * Options of the items + * @param {number} index - Rendered index + * @return {@link VirtualScroller.VirtualScrollerItemOptions} + */ + getItemOptions(index: number): VirtualScrollerItemOptions; + }) => VNode[]; + /** + * Custom loader template. + * @param {Object} scope - loader slot's params. + */ + loader: (scope: { + /** + * Options of the loader items for virtualscroller + */ + options: any[]; + }) => VNode[]; +} + +export declare type ListboxEmits = { + /** + * Emitted when the value changes. + * @param {*} value - New value. + */ + 'update:modelValue': (value: any) => void; + /** + * Callback to invoke on value change. + * @param {ListboxChangeEvent} event - Custom change event. + */ + 'change': (event: ListboxChangeEvent) => void; + /** + * Callback to invoke when the component receives focus. + * @param {Event} event - Browser event. + */ + 'focus': (event: Event) => void; + /** + * Callback to invoke when the component loses focus. + * @param {Event} event - Browser event. + */ + 'blur': (event: Event) => void; + /** + * Callback to invoke on filter input. + * @param {ListboxFilterEvent} event - Custom filter event. + */ + 'filter': (event: ListboxFilterEvent) => void; +} + +declare class Listbox extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Listbox: GlobalComponentConstructor + } +} + +/** + * + * Listbox is used to select one or more values from a list of items. + * + * Demos: + * + * - [Listbox](https://www.primefaces.org/primevue/showcase/#/listbox) + * + */ +export default Listbox; diff --git a/components/listbox/Listbox.spec.js b/components/listbox/Listbox.spec.js new file mode 100644 index 000000000..6cdf8e9d3 --- /dev/null +++ b/components/listbox/Listbox.spec.js @@ -0,0 +1,38 @@ +import { mount } from '@vue/test-utils'; +import Listbox from './Listbox.vue'; + +describe('Listbox.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Listbox, { + props: { + modelValue: null, + options: [ + {name: 'New York', code: 'NY'}, + {name: 'Rome', code: 'RM'}, + {name: 'London', code: 'LDN'}, + {name: 'Istanbul', code: 'IST'}, + {name: 'Paris', code: 'PRS'} + ], + optionLabel: 'name' + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-listbox.p-component').exists()).toBe(true); + expect(wrapper.findAll('li.p-listbox-item').length).toBe(5); + expect(wrapper.findAll('li.p-listbox-item')[0].attributes()['aria-label']).toBe('New York'); + }); + + it('should select a list item', async () => { + await wrapper.vm.onOptionSelect({}, wrapper.vm.options[0]); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual([wrapper.vm.options[0]]); + + await wrapper.setProps({ modelValue: wrapper.vm.options[0] }); + + expect(wrapper.findAll('li.p-listbox-item')[0].classes()).toContain('p-highlight'); + }); +}); \ No newline at end of file diff --git a/components/listbox/Listbox.vue b/components/listbox/Listbox.vue new file mode 100755 index 000000000..0cdece29c --- /dev/null +++ b/components/listbox/Listbox.vue @@ -0,0 +1,737 @@ + + + + + diff --git a/components/listbox/package.json b/components/listbox/package.json new file mode 100644 index 000000000..de768db0e --- /dev/null +++ b/components/listbox/package.json @@ -0,0 +1,9 @@ +{ + "main": "./listbox.cjs.js", + "module": "./listbox.esm.js", + "unpkg": "./listbox.min.js", + "types": "./Listbox.d.ts", + "browser": { + "./sfc": "./Listbox.vue" + } +} \ No newline at end of file diff --git a/components/megamenu/MegaMenu.d.ts b/components/megamenu/MegaMenu.d.ts new file mode 100755 index 000000000..9f5bee027 --- /dev/null +++ b/components/megamenu/MegaMenu.d.ts @@ -0,0 +1,70 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; +import { MenuItem } from '../menuitem'; + +type MegaMenuOrientationType = 'horizontal' | 'vertical' | undefined; + +export interface MegaMenuProps { + /** + * An array of menuitems. + */ + model?: MenuItem[] | undefined; + /** + * Defines the orientation. + * @see MegaMenuOrientationType + * Default value is 'horizontal'. + */ + orientation?: MegaMenuOrientationType; + /** + * Whether to apply 'router-link-active-exact' class if route exactly matches the item path. + * Default value is true. + */ + exact?: boolean | undefined; +} + +export interface MegaMenuSlots { + /** + * Custom start template. + */ + start: () => VNode[]; + /** + * Custom end template. + */ + end: () => VNode[]; + /** + * Custom item template. + * @param {Object} scope - item slot's params. + */ + item: (scope: { + /** + * Menuitem instance + */ + item: MenuItem; + }) => VNode[]; +} + +export declare type MegaMenuEmits = { +} + +declare class MegaMenu extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + MegaMenu: GlobalComponentConstructor + } +} + +/** + * + * MegaMenu is navigation component that displays submenus together. + * + * Helper API: + * + * - [MenuItem](https://www.primefaces.org/primevue/showcase/#/menumodel) + * + * Demos: + * + * - [MegaMenu](https://www.primefaces.org/primevue/showcase/#/megamenu) + * + */ +export default MegaMenu; diff --git a/components/megamenu/MegaMenu.spec.js b/components/megamenu/MegaMenu.spec.js new file mode 100644 index 000000000..2c077a40f --- /dev/null +++ b/components/megamenu/MegaMenu.spec.js @@ -0,0 +1,88 @@ +import { mount } from '@vue/test-utils'; +import MegaMenu from './MegaMenu.vue'; + +describe('MegaMenu.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(MegaMenu, { + global: { + stubs: { + 'router-link': true + } + }, + props: { + model: [ + { + label: 'Videos', icon: 'pi pi-fw pi-video', + items: [ + [ + { + label: 'Video 1', + items: [{label: 'Video 1.1'}, {label: 'Video 1.2'}] + }, + { + label: 'Video 2', + items: [{label: 'Video 2.1'}, {label: 'Video 2.2'}] + } + ] + ] + }, + { + label: 'Users', icon: 'pi pi-fw pi-users', + items: [ + [ + { + label: 'User 1', + items: [{label: 'User 1.1'}, {label: 'User 1.2'}] + } + ], + [ + { + label: 'User 2', + items: [{label: 'User 2.1'}, {label: 'User 2.2'}] + }, + { + label: 'User 3', + items: [{label: 'User 3.1'}, {label: 'User 3.2'}] + } + ] + ] + } + ] + } + }); + }); + + it('should exists', () => { + expect(wrapper.find('.p-megamenu.p-component').exists()).toBe(true); + expect(wrapper.find('.p-megamenu-root-list').exists()).toBe(true); + expect(wrapper.findAll('ul.p-megamenu-submenu').length).toBe(5); + expect(wrapper.findAll('li.p-menuitem').length).toBe(12); + expect(wrapper.findAll('li.p-menuitem')[0].findAll('span.p-menuitem-text')[0].text()).toBe('Videos'); + expect(wrapper.findAll('li.p-megamenu-submenu-header')[0].text()).toBe('Video 1'); + expect(wrapper.findAll('li.p-menuitem')[1].findAll('span.p-menuitem-text')[0].text()).toBe('Video 1.1'); + }); + + it('should select item', async () => { + const firstItem = wrapper.findAll('li.p-menuitem')[0]; + + await wrapper.vm.onCategoryClick({}, wrapper.vm.model[0]); + + expect(firstItem.classes()).toContain('p-menuitem-active'); + }); + + it('should deselect item', async () => { + const firstItem = wrapper.findAll('li.p-menuitem')[0]; + + await wrapper.vm.onCategoryClick({}, wrapper.vm.model[0].items[0][0].items[0]); + + expect(firstItem.classes()).not.toContain('p-menuitem-active'); + }); + + it('should orientation work', async () => { + await wrapper.setProps({ orientation: 'vertical' }); + + expect(wrapper.find('.p-megamenu-vertical').exists()).toBe(true); + }); +}); \ No newline at end of file diff --git a/components/megamenu/MegaMenu.vue b/components/megamenu/MegaMenu.vue new file mode 100755 index 000000000..3a2f7a2f5 --- /dev/null +++ b/components/megamenu/MegaMenu.vue @@ -0,0 +1,431 @@ + + + + + diff --git a/components/megamenu/package.json b/components/megamenu/package.json new file mode 100644 index 000000000..393c90462 --- /dev/null +++ b/components/megamenu/package.json @@ -0,0 +1,9 @@ +{ + "main": "./megamenu.cjs.js", + "module": "./megamenu.esm.js", + "unpkg": "./megamenu.min.js", + "types": "./MegaMenu.d.ts", + "browser": { + "./sfc": "./MegaMenu.vue" + } +} \ No newline at end of file diff --git a/components/menu/Menu.d.ts b/components/menu/Menu.d.ts new file mode 100755 index 000000000..829923f31 --- /dev/null +++ b/components/menu/Menu.d.ts @@ -0,0 +1,94 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; +import { MenuItem } from '../menuitem'; + +type MenuAppendToType = 'body' | 'self' | string | undefined | HTMLElement; + +export interface MenuProps { + /** + * An array of menuitems. + */ + model?: MenuItem[] | undefined; + /** + * Defines if menu would displayed as a popup. + */ + popup?: boolean | undefined; + /** + * A valid query selector or an HTMLElement to specify where the overlay gets attached. + * @see MenuAppendToType + * Default value is 'body'. + */ + appendTo?: MenuAppendToType; + /** + * Whether to automatically manage layering. + * Default value is true. + */ + autoZIndex?: boolean | undefined; + /** + * Base zIndex value to use in layering. + * Default value is 0. + */ + baseZIndex?: number | undefined; + /** + * Whether to apply 'router-link-active-exact' class if route exactly matches the item path. + * Default value is true. + */ + exact?: boolean | undefined; +} + +export interface MenuSlots { + /** + * Custom item template. + * @param {Object} scope - item slot's params. + */ + item: (scope: { + item: MenuItem; + }) => VNode[]; +} + +export declare type MenuEmits = { +} + +declare class Menu extends ClassComponent { + /** + * Toggles the visibility of the overlay. + * @param {Event} event - Browser event. + * + * @memberof Menu + */ + toggle: (event: Event) => void; + /** + * Shows the overlay. + * @param {Event} event - Browser event. + * + * @memberof Menu + */ + show: (event: Event, target?: any) => void; + /** + * Hides the overlay. + * + * @memberof Menu + */ + hide(): void; +} + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Menu: GlobalComponentConstructor + } +} + +/** + * + * Menu is a navigation / command component that supports dynamic and static positioning. + * + * Helper API: + * + * - [MenuItem](https://www.primefaces.org/primevue/showcase/#/menumodel) + * + * Demos: + * + * - [Menu](https://www.primefaces.org/primevue/showcase/#/menu) + * + */ +export default Menu; diff --git a/components/menu/Menu.spec.js b/components/menu/Menu.spec.js new file mode 100644 index 000000000..758f1ae3e --- /dev/null +++ b/components/menu/Menu.spec.js @@ -0,0 +1,70 @@ +import { mount } from '@vue/test-utils'; +import PrimeVue from '@/components/config/PrimeVue'; +import Menu from './Menu.vue'; + +describe('Menu.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Menu, { + global: { + plugins: [PrimeVue], + stubs: { + 'router-link': true, + teleport: true + } + }, + props: { + model: [ + { + label: 'Options', + items: [{ + label: 'Update', + icon: 'pi pi-refresh', + command: () => { + this.$toast.add({severity:'success', summary:'Updated', detail:'Data Updated', life: 3000}); + } + }, + { + label: 'Delete', + icon: 'pi pi-times', + command: () => { + this.$toast.add({ severity: 'warn', summary: 'Delete', detail: 'Data Deleted', life: 3000}); + } + } + ]}, + { + label: 'Navigate', + items: [{ + label: 'Vue Website', + icon: 'pi pi-external-link', + url: 'https://vuejs.org/' + }, + { + label: 'Router', + icon: 'pi pi-upload', + to: '/fileupload' + } + ]} + ] + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-menu.p-component').exists()).toBe(true); + expect(wrapper.findAll('.p-submenu-header').length).toBe(2); + expect(wrapper.findAll('.p-submenu-header')[0].text()).toBe('Options'); + expect(wrapper.findAll('.p-menuitem').length).toBe(4); + expect(wrapper.findAll('.p-menuitem')[0].find('span.p-menuitem-text').text()).toBe('Update'); + expect(wrapper.findAll('.p-menuitem')[2].find('a').attributes().href).toBe('https://vuejs.org/'); + }); + + it('should popup work', async () => { + await wrapper.setProps({ popup: true }); + + await wrapper.vm.toggle({}); + + expect(wrapper.find('.p-menu.p-component').exists()).toBe(true); + }); +}); \ No newline at end of file diff --git a/components/menu/Menu.vue b/components/menu/Menu.vue new file mode 100755 index 000000000..d0e100c5e --- /dev/null +++ b/components/menu/Menu.vue @@ -0,0 +1,255 @@ + + + + + diff --git a/components/menu/Menuitem.vue b/components/menu/Menuitem.vue new file mode 100755 index 000000000..93d45a9d5 --- /dev/null +++ b/components/menu/Menuitem.vue @@ -0,0 +1,65 @@ + + + diff --git a/components/menu/package.json b/components/menu/package.json new file mode 100644 index 000000000..c06306291 --- /dev/null +++ b/components/menu/package.json @@ -0,0 +1,9 @@ +{ + "main": "./menu.cjs.js", + "module": "./menu.esm.js", + "unpkg": "./menu.min.js", + "types": "./Menu.d.ts", + "browser": { + "./sfc": "./Menu.vue" + } +} \ No newline at end of file diff --git a/components/menubar/Menubar.d.ts b/components/menubar/Menubar.d.ts new file mode 100755 index 000000000..467057537 --- /dev/null +++ b/components/menubar/Menubar.d.ts @@ -0,0 +1,62 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; +import { MenuItem } from '../menuitem'; + +export interface MenubarProps { + /** + * An array of menuitems. + */ + model?: MenuItem[] | undefined; + /** + * Whether to apply 'router-link-active-exact' class if route exactly matches the item path. + * Default value is true. + */ + exact?: boolean | undefined; +} + +export interface MenubarSlots { + /** + * Custom start template. + */ + start: () => VNode[]; + /** + * Custom end template. + */ + end: () => VNode[]; + /** + * Custom item template. + * @param {Object} scope - item slot's params. + */ + item: (scope: { + /** + * Menuitem instance + */ + item: MenuItem; + }) => VNode[]; +} + +export declare type MenubarEmits = { +} + +declare class Menubar extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Menubar: GlobalComponentConstructor + } +} + +/** + * + * Menubar is a horizontal menu component. + * + * Helper API: + * + * - [MenuItem](https://www.primefaces.org/primevue/showcase/#/menumodel) + * + * Demos: + * + * - [Menubar](https://www.primefaces.org/primevue/showcase/#/menubar) + * + */ +export default Menubar; diff --git a/components/menubar/Menubar.spec.js b/components/menubar/Menubar.spec.js new file mode 100644 index 000000000..006eeed1a --- /dev/null +++ b/components/menubar/Menubar.spec.js @@ -0,0 +1,74 @@ +import { mount } from '@vue/test-utils'; +import Menubar from './Menubar.vue'; + +describe('Menubar.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Menubar, { + global: { + stubs: { + 'router-link': true + } + }, + props: { + model: [ + { + label:'File', + icon:'pi pi-fw pi-file', + items:[ + { + label:'New', + icon:'pi pi-fw pi-plus', + items:[ + { + label:'Bookmark', + icon:'pi pi-fw pi-bookmark' + }, + { + label:'Video', + icon:'pi pi-fw pi-video' + } + ] + }, + { + label:'Delete', + icon:'pi pi-fw pi-trash' + }, + { + separator:true + }, + { + label:'Export', + icon:'pi pi-fw pi-external-link' + } + ] + }, + { + label:'Quit', + icon:'pi pi-fw pi-power-off' + } + ] + }, + slots: { + start: 'Start Slot', + end: 'End Slot' + } + }) + }); + + it('should exist', () => { + expect(wrapper.find('.p-menubar.p-component').exists()).toBe(true); + expect(wrapper.find('.p-menubar-root-list').exists()).toBe(true); + expect(wrapper.findAll('ul.p-submenu-list').length).toBe(2); + expect(wrapper.findAll('ul.p-submenu-list')[0].findAll('li.p-menuitem')[0].find('.p-menuitem-text').text()).toBe('New'); + expect(wrapper.findAll('li.p-menuitem').length).toBe(7); + expect(wrapper.findAll('li.p-menu-separator').length).toBe(1); + }); + + it('should slot visible', () => { + expect(wrapper.find('.p-menubar-start').exists()).toBe(true); + expect(wrapper.find('.p-menubar-end').exists()).toBe(true); + expect(wrapper.find('.p-menubar-end').text()).toBe('End Slot'); + }); +}); \ No newline at end of file diff --git a/components/menubar/Menubar.vue b/components/menubar/Menubar.vue new file mode 100755 index 000000000..791836c80 --- /dev/null +++ b/components/menubar/Menubar.vue @@ -0,0 +1,164 @@ + + + + + diff --git a/components/menubar/MenubarSub.vue b/components/menubar/MenubarSub.vue new file mode 100755 index 000000000..10827b925 --- /dev/null +++ b/components/menubar/MenubarSub.vue @@ -0,0 +1,306 @@ + + + diff --git a/components/menubar/package.json b/components/menubar/package.json new file mode 100644 index 000000000..594b6a12f --- /dev/null +++ b/components/menubar/package.json @@ -0,0 +1,9 @@ +{ + "main": "./menubar.cjs.js", + "module": "./menubar.esm.js", + "unpkg": "./menubar.min.js", + "types": "./Menubar.d.ts", + "browser": { + "./sfc": "./Menubar.vue" + } +} \ No newline at end of file diff --git a/components/menuitem/MenuItem.d.ts b/components/menuitem/MenuItem.d.ts new file mode 100644 index 000000000..f2c5a8c30 --- /dev/null +++ b/components/menuitem/MenuItem.d.ts @@ -0,0 +1,82 @@ +import { RouteLocationRaw } from "vue-router"; + +type MenuItemLabelType = string | ((...args: any) => string) | undefined; + +type MenuItemDisabledType = boolean | ((...args: any) => boolean) | undefined; + +type MenuItemVisibleType = boolean | ((...args: any) => boolean) | undefined; + +export interface MenuItemCommandEvent { + /** + * Browser event. + */ + originalEvent: Event; + /** + * Menuitem instance. + */ + item: MenuItem; + /** + * Optional + */ + [key: string]: any; +} + +export interface MenuItem { + /** + * Property name or getter function to use as the label of an item. + */ + label?: MenuItemLabelType; + /** + * Icon of the item. + */ + icon?: string | undefined; + /** + * Route configuration such as path, name and parameters. + */ + to?: RouteLocationRaw | undefined; + /** + * Callback to execute when item is clicked. + * @param {@link MenuItemCommandEvent} event - Custom command event. + */ + command?: (event: MenuItemCommandEvent) => void; + /** + * External link to navigate when item is clicked. + */ + url?: string | undefined; + /** + * An array of children menuitems. + */ + items?: MenuItem[] | undefined; + /** + * A boolean or a function to return a boolean to specify if the item is disabled. + */ + disabled?: MenuItemDisabledType; + /** + * A boolean or a function to return a boolean to specify if the item is visible. + */ + visible?: MenuItemVisibleType; + /** + * Specifies where to open the linked document. + */ + target?: string | undefined; + /** + * Defines the item as a separator. + */ + separator?: boolean | undefined; + /** + * Inline style of the menuitem. + */ + style?: any; + /** + * Style class of the menuitem. + */ + class?: any; + /** + * Unique identifier of an item. + */ + key?: string | undefined + /** + * Optional + */ + [key: string]: any; +} diff --git a/components/menuitem/package.json b/components/menuitem/package.json new file mode 100644 index 000000000..dfb77615c --- /dev/null +++ b/components/menuitem/package.json @@ -0,0 +1,3 @@ +{ + "types": "./MenuItem.d.ts" +} diff --git a/components/message/Message.d.ts b/components/message/Message.d.ts new file mode 100755 index 000000000..7b3174222 --- /dev/null +++ b/components/message/Message.d.ts @@ -0,0 +1,65 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type MessageSeverityType = 'success' | 'info' | 'warn' | 'error' | undefined; + +export interface MessageProps { + /** + * Severity level of the message. + * @see MessageSeverityType + * Default value is 'info'. + */ + severity?: MessageSeverityType; + /** + * Whether the message can be closed manually using the close icon. + * Default value is true. + */ + closable?: boolean | undefined; + /** + * When enabled, message is not removed automatically. + */ + sticky?: boolean | undefined; + /** + * Delay in milliseconds to close the message automatically. + * Default value is 3000. + */ + life?: number | undefined; + /** + * Display a custom icon for the message. + */ + icon?: string | undefined; +} + +export interface MessageSlots { + /** + * Default custom slot. + */ + default: () => VNode[]; +} + +export declare type MessageEmits = { + /** + * Callback to invoke when a message is closed. + * @param {Event} event - Browser event. + */ + 'close': (event: Event) => void; +} + +declare class Message extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Message: GlobalComponentConstructor + } +} + +/** + * + * Messages is used to display inline messages with various severities. + * + * Demos: + * + * - [Message](https://www.primefaces.org/primevue/showcase/#/message) + * + */ +export default Message; diff --git a/components/message/Message.spec.js b/components/message/Message.spec.js new file mode 100644 index 000000000..78e2707fd --- /dev/null +++ b/components/message/Message.spec.js @@ -0,0 +1,52 @@ +import { mount } from '@vue/test-utils'; +import Message from './Message.vue'; + +describe('Message.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Message, { + props: { + severity: 'error' + }, + slots: { + default: 'Error Message Content' + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-message.p-component').exists()).toBe(true); + expect(wrapper.find('.p-message.p-component').classes()).toContain('p-message-error'); + expect(wrapper.find('.p-message-text').text()).toContain('Error Message Content'); + }); + + it('should close the message', async () => { + await wrapper.vm.close({}); + + expect(wrapper.vm.visible).toBe(false); + expect(wrapper.emitted().close[0]).toEqual([{}]); + }); +}); + +describe('Message.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Message, { + props: { + severity: 'error', + life: 3000, + sticky: false + }, + slots: { + default: 'Error Message Content' + } + }); + }); + + it('should sticky and life works', async () => { + jest.runTimersToTime(3001); + expect(wrapper.vm.visible).toBe(false); + }); +}); \ No newline at end of file diff --git a/components/message/Message.vue b/components/message/Message.vue new file mode 100755 index 000000000..0f45e7cbc --- /dev/null +++ b/components/message/Message.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/components/message/package.json b/components/message/package.json new file mode 100644 index 000000000..c67a499e9 --- /dev/null +++ b/components/message/package.json @@ -0,0 +1,9 @@ +{ + "main": "./message.cjs.js", + "module": "./message.esm.js", + "unpkg": "./message.min.js", + "types": "./Message.d.ts", + "browser": { + "./sfc": "./Message.vue" + } +} \ No newline at end of file diff --git a/components/multiselect/MultiSelect.d.ts b/components/multiselect/MultiSelect.d.ts new file mode 100755 index 000000000..0ef65d612 --- /dev/null +++ b/components/multiselect/MultiSelect.d.ts @@ -0,0 +1,455 @@ +import { ButtonHTMLAttributes, HTMLAttributes, InputHTMLAttributes, VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; +import { VirtualScrollerProps, VirtualScrollerItemOptions } from '../virtualscroller'; + +type MultiSelectOptionLabelType = string | ((data: any) => string) | undefined; + +type MultiSelectOptionValueType = string | ((data: any) => any) | undefined; + +type MultiSelectOptionDisabledType = string | ((data: any) => boolean) | undefined; + +type MultiSelectOptionChildrenType = string | ((data: any) => any[]) | undefined; + +type MultiSelectFilterMatchModeType = 'contains' | 'startsWith' | 'endsWith' | undefined; + +type MultiSelectAppendToType = 'body' | 'self' | string | undefined | HTMLElement; + +type MultiSelectDisplayType = 'comma' | 'chip' | undefined; + +export interface MultiSelectChangeEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Selected option value + */ + value: any; +} + +export interface MultiSelectAllChangeEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Whether all data is selected. + */ + checked: boolean; +} + +export interface MultiSelectFilterEvent { + /** + * Original event + */ + originalEvent: Event; + /** + * Filter value + */ + value: string; +} + +export interface MultiSelectProps { + /** + * Value of the component. + */ + modelValue?: any; + /** + * An array of selectitems to display as the available options. + */ + options?: any[] | undefined; + /** + * Property name or getter function to use as the label of an option. + */ + optionLabel?: MultiSelectOptionLabelType; + /** + * Property name or getter function to use as the value of an option, defaults to the option itself when not defined. + */ + optionValue?: MultiSelectOptionValueType; + /** + * Property name or getter function to use as the disabled flag of an option, defaults to false when not defined. + */ + optionDisabled?: MultiSelectOptionDisabledType; + /** + * Property name or getter function to use as the label of an option group. + */ + optionGroupLabel?: MultiSelectOptionLabelType; + /** + * Property name or getter function that refers to the children options of option group. + */ + optionGroupChildren?: MultiSelectOptionChildrenType; + /** + * Height of the viewport, a scrollbar is defined if height of list exceeds this value. + * Default value is '200px'. + */ + scrollHeight?: string | undefined; + /** + * Label to display when there are no selections. + */ + placeholder?: string | undefined; + /** + * When present, it specifies that the component should be disabled. + */ + disabled?: boolean | undefined; + /** + * Identifier of the underlying input element. + */ + inputId?: string | undefined; + /** + * Uses to pass all properties of the HTMLInputElement to the focusable input element inside the component. + */ + inputProps?: InputHTMLAttributes | undefined; + /** + * Inline style of the overlay panel. + */ + panelStyle?: any; + /** + * Style class of the overlay panel. + */ + panelClass?: any; + /** + * Uses to pass all properties of the HTMLDivElement to the overlay panel. + */ + panelProps?: HTMLAttributes | undefined; + /** + * Uses to pass all properties of the HTMLInputElement to the filter input inside the overlay panel. + */ + filterInputProps?: InputHTMLAttributes | undefined; + /** + * Uses to pass all properties of the HTMLButtonElement to the clear button inside the overlay panel. + */ + closeButtonProps?: ButtonHTMLAttributes | undefined; + /** + * A property to uniquely identify an option. + */ + dataKey?: string | undefined; + /** + * When specified, displays a filter input at header. + */ + filter?: boolean | undefined; + /** + * Placeholder text to show when filter input is empty. + */ + filterPlaceholder?: string | undefined; + /** + * Locale to use in filtering. The default locale is the host environment's current locale. + */ + filterLocale?: string | undefined; + /** + * Defines the filtering algorithm to use when searching the options. + * @see MultiSelectFilterMatchModeType + * Default value is 'contains'. + */ + filterMatchMode?: MultiSelectFilterMatchModeType; + /** + * Fields used when filtering the options, defaults to optionLabel. + */ + filterFields?: string[] | undefined; + /** + * A valid query selector or an HTMLElement to specify where the overlay gets attached. Special keywords are 'body' for document body and 'self' for the element itself. + * @see MultiSelectAppendToType + * Default value is 'body'. + */ + appendTo?: MultiSelectAppendToType; + /** + * Defines how the selected items are displayed. + * @see MultiSelectDisplayType + * Default value is 'comma'. + */ + display?: MultiSelectDisplayType; + /** + * Label to display after exceeding max selected labels. + * Default value is '{0} items selected'. + */ + selectedItemsLabel?: string | undefined; + /** + * Decides how many selected item labels to show at most. + */ + maxSelectedLabels?: number | undefined; + /** + * Maximum number of selectable items. + */ + selectionLimit?: number | undefined; + /** + * Whether to show the header checkbox to toggle the selection of all items at once. + * Default value is true. + */ + showToggleAll?: boolean | undefined; + /** + * Whether the multiselect is in loading state. + */ + loading?: boolean | undefined; + /** + * Icon to display in loading state. + * Default value is 'pi pi-spinner pi-spin'. + */ + loadingIcon?: string | undefined; + /** + * Whether all data is selected. + */ + selectAll?: boolean | undefined; + /** + * Clears the filter value when hiding the dropdown. + */ + resetFilterOnHide?: boolean; + /** + * Whether to use the virtualScroller feature. The properties of VirtualScroller component can be used like an object in it. + * @see VirtualScroller.VirtualScrollerProps + */ + virtualScrollerOptions?: VirtualScrollerProps; + /** + * Whether to focus on the first visible or selected element when the overlay panel is shown. + * Default value is true. + */ + autoOptionFocus?: boolean | undefined; + /** + * Text to be displayed in hidden accessible field when filtering returns any results. Defaults to value from PrimeVue locale configuration. + * Default value is '{0} results are available'. + */ + filterMessage?: string | undefined; + /** + * Text to be displayed in hidden accessible field when options are selected. Defaults to value from PrimeVue locale configuration. + * Default value is '{0} items selected'. + */ + selectionMessage?: string | undefined; + /** + * Text to be displayed in hidden accessible field when any option is not selected. Defaults to value from PrimeVue locale configuration. + * Default value is 'No selected item'. + */ + emptySelectionMessage?: string | undefined; + /** + * Text to display when filtering does not return any results. Defaults to value from PrimeVue locale configuration. + * Default value is 'No results found'. + */ + emptyFilterMessage?: string | undefined; + /** + * Text to display when there are no options available. Defaults to value from PrimeVue locale configuration. + * Default value is 'No results found'. + */ + emptyMessage?: string | undefined; + /** + * Index of the element in tabbing order. + */ + tabindex?: number | string | undefined; + /** + * Defines a string value that labels an interactive element. + */ + "aria-label"?: string | undefined; + /** + * Identifier of the underlying input element. + */ + "aria-labelledby"?: string | undefined; +} + +export interface MultiSelectSlots { + /** + * Custom value template. + * @param {Object} scope - value slot's params. + */ + value: (scope: { + /** + * Value of the component + */ + value: any; + /** + * Placeholder prop value + */ + placeholder: string; + }) => VNode[]; + /** + * Custom chip template. + * @param {Object} scope - chip slot's params. + */ + chip: (scope: { + /** + * A value in the selection + */ + value: any; + }) => VNode[]; + /** + * Custom indicator template. + */ + indicator: () => VNode[]; + /** + * Custom header template. + * @param {Object} scope - header slot's params. + */ + header: (scope: { + /** + * Value of the component + */ + value: any; + /** + * Displayed options + */ + options: any[]; + }) => VNode[]; + /** + * Custom footer template. + * @param {Object} scope - footer slot's params. + */ + footer: (scope: { + /** + * Value of the component + */ + value: any; + /** + * Displayed options + */ + options: any[]; + }) => VNode[]; + /** + * Custom option template. + * @param {Object} scope - option slot's params. + */ + option: (scope: { + /** + * Option instance + */ + option: any; + /** + * Index of the option + */ + index: number; + }) => VNode[]; + /** + * Custom option group template. + * @param {Object} scope - option group slot's params. + */ + optiongroup: (scope: { + /** + * Option instance + */ + option: any; + /** + * Index of the option + */ + index: number; + }) => VNode[]; + /** + * Custom emptyfilter template. + */ + emptyfilter: () => VNode[]; + /** + * Custom empty template. + */ + empty: () => VNode[]; + /** + * Custom content template. + * @param {Object} scope - content slot's params. + */ + content: (scope: { + /** + * An array of objects to display for virtualscroller + */ + items: any; + /** + * Style class of the component + */ + styleClass: string; + /** + * Referance of the content + * @param {HTMLElement} el - Element of 'ref' property + */ + contentRef(el: any): void; + /** + * Options of the items + * @param {number} index - Rendered index + * @return {@link VirtualScroller.VirtualScrollerItemOptions} + */ + getItemOptions(index: number): VirtualScrollerItemOptions; + }) => VNode[]; + /** + * Custom loader template. + * @param {Object} scope - loader slot's params. + */ + loader: (scope: { + /** + * Options of the loader items for virtualscroller + */ + options: any[]; + }) => VNode[]; +} + +export declare type MultiSelectEmits = { + /** + * Emitted when the value changes. + * @param {*} value - New value. + */ + 'update:modelValue': (value: any) => void; + /** + * Callback to invoke on value change. + * @param {MultiSelectChangeEvent} event - Custom change event. + */ + 'change': (event: MultiSelectChangeEvent) => void; + /** + * Callback to invoke when the component receives focus. + * @param {Event} event - Browser event. + */ + 'focus': (event: Event) => void; + /** + * Callback to invoke when the component loses focus. + * @param {Event} event - Browser event. + */ + 'blur': (event: Event) => void; + /** + * Callback to invoke before the overlay is shown. + */ + 'before-show': () => void; + /** + * Callback to invoke before the overlay is hidden. + */ + 'before-hide': () => void; + /** + * Callback to invoke when the overlay is shown. + */ + 'show': () => void; + /** + * Callback to invoke when the overlay is hidden. + */ + 'hide': () => void; + /** + * Callback to invoke on filter input. + * @param {MultiSelectFilterEvent} event - Custom filter event. + */ + 'filter': (event: MultiSelectFilterEvent) => void; + /** + * Callback to invoke when all data is selected. + * @param {MultiSelectAllChangeEvent} event - Custom select all change event. + */ + 'selectall-change': (event: MultiSelectAllChangeEvent) => void; +} + +declare class MultiSelect extends ClassComponent { + /** + * Shows the overlay. + * @param {boolean} [isFocus] - Decides whether to focus on the component. Default value is false. + * + * @memberof MultiSelect + */ + show: (isFocus?: boolean) => void; + /** + * Hides the overlay. + * @param {boolean} [isFocus] - Decides whether to focus on the component. Default value is false. + * + * @memberof MultiSelect + */ + hide: (isFocus?: boolean) => void; +} + +declare module '@vue/runtime-core' { + interface GlobalComponents { + MultiSelect: GlobalComponentConstructor + } +} + +/** + * + * MultiSelect is used to multiple values from a list of options. + * + * Demos: + * + * - [MultiSelect](https://www.primefaces.org/primevue/showcase/#/multiselect) + * + */ +export default MultiSelect; diff --git a/components/multiselect/MultiSelect.spec.js b/components/multiselect/MultiSelect.spec.js new file mode 100644 index 000000000..7f063b974 --- /dev/null +++ b/components/multiselect/MultiSelect.spec.js @@ -0,0 +1,78 @@ +import { mount } from '@vue/test-utils'; +import PrimeVue from '@/components/config/PrimeVue'; +import MultiSelect from './MultiSelect.vue'; + +describe('MultiSelect.vue', () => { + let wrapper; + + beforeEach(async () => { + wrapper = mount(MultiSelect, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true + } + }, + props: { + modelValue: null, + options: [ + {name: 'New York', code: 'NY'}, + {name: 'Rome', code: 'RM'}, + {name: 'London', code: 'LDN'}, + {name: 'Istanbul', code: 'IST'}, + {name: 'Paris', code: 'PRS'} + ], + optionLabel: 'name', + placeholder: 'Select Cities' + } + }); + + await wrapper.vm.onClick({}); + }); + + it('should exist', () => { + expect(wrapper.find('.p-multiselect.p-component').exists()).toBe(true); + expect(wrapper.find('.p-multiselect-label.p-placeholder').text()).toBe('Select Cities'); + expect(wrapper.find('.p-multiselect-panel').exists()).toBe(true); + expect(wrapper.findAll('li.p-multiselect-item').length).toBe(5); + expect(wrapper.findAll('li.p-multiselect-item')[0].attributes()['aria-label']).toBe('New York'); + expect(wrapper.findAll('li.p-multiselect-item')[0].findAll('span')[1].text()).toBe('New York'); + }); + + it('should select an item', async () => { + await wrapper.vm.onOptionSelect({}, wrapper.vm.options[0]); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual([[wrapper.vm.options[0]]]); + + await wrapper.setProps({ modelValue: [wrapper.vm.options[0]]}); + + expect(wrapper.findAll('li.p-multiselect-item')[0].classes()).toContain('p-highlight'); + expect(wrapper.find('.p-multiselect-label').text()).toBe('New York'); + }); + + it('should select multiple item', async () => { + await wrapper.setProps({ modelValue: [wrapper.vm.options[0]]}); + + await wrapper.vm.onOptionSelect({}, wrapper.vm.options[1]); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual([[wrapper.vm.options[0], wrapper.vm.options[1]]]); + + await wrapper.setProps({ modelValue: [wrapper.vm.options[0], wrapper.vm.options[1]]}); + + expect(wrapper.findAll('li.p-multiselect-item')[0].classes()).toContain('p-highlight'); + expect(wrapper.findAll('li.p-multiselect-item')[1].classes()).toContain('p-highlight'); + }); + + it('should close panel', async () => { + await wrapper.vm.onCloseClick(); + + expect(wrapper.find('.p-multiselect-panel').exists()).toBe(false); + }); + + it('should chip work', async () => { + await wrapper.setProps({ display: 'chip', modelValue: [wrapper.vm.options[0]] }); + + expect(wrapper.find('.p-multiselect-token').exists()).toBe(true); + expect(wrapper.find('.p-multiselect-token-label').text()).toBe('New York'); + }); +}); \ No newline at end of file diff --git a/components/multiselect/MultiSelect.vue b/components/multiselect/MultiSelect.vue new file mode 100755 index 000000000..e427a4def --- /dev/null +++ b/components/multiselect/MultiSelect.vue @@ -0,0 +1,1160 @@ + + + + + diff --git a/components/multiselect/package.json b/components/multiselect/package.json new file mode 100644 index 000000000..c2709d035 --- /dev/null +++ b/components/multiselect/package.json @@ -0,0 +1,9 @@ +{ + "main": "./multiselect.cjs.js", + "module": "./multiselect.esm.js", + "unpkg": "./multiselect.min.js", + "types": "./MultiSelect.d.ts", + "browser": { + "./sfc": "./MultiSelect.vue" + } +} \ No newline at end of file diff --git a/components/orderlist/OrderList.d.ts b/components/orderlist/OrderList.d.ts new file mode 100755 index 000000000..7e22d6c75 --- /dev/null +++ b/components/orderlist/OrderList.d.ts @@ -0,0 +1,139 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface OrderListReorderEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Ordered list + */ + value: any[]; + /** + * Direction of the change; 'up', 'down', 'bottom', 'top' + */ + direction: string; +} + +export interface OrderListSelectionChangeEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Ordered list + */ + value: any[]; +} + +export interface OrderListProps { + /** + * Value of the component. + */ + modelValue?: any[]; + /** + * Name of the field that uniquely identifies the a record in the data. + */ + dataKey?: string | undefined; + /** + * Selected items in the list. + */ + selection?: any[]; + /** + * Defines whether metaKey is requred or not for the selection. + * When true metaKey needs to be pressed to select or unselect an item and + * when set to false selection of each item can be toggled individually. On touch enabled devices, metaKeySelection is turned off automatically. + * Default value is true. + */ + metaKeySelection?: boolean | undefined; + /** + * Inline style of the the list element. + */ + listStyle?: any; + /** + * Whether the list optimizes layout based on screen size. + * Default value is true. + */ + responsive?: boolean | undefined; + /** + * The breakpoint to define the maximum width boundary when responsiveness is enabled. + * Default value is '960px'. + */ + breakpoint?: string | undefined; + /** + * Whether to displays rows with alternating colors. + */ + stripedRows?: boolean | undefined; +} + +export interface OrderListSlots { + /** + * Custom header template. + */ + header: () => VNode[]; + /** + * Custom item template. + * @param {Object} scope - item slot's params. + */ + item: (scope: { + /** + * Item of the component + */ + item: any; + /** + * Index of the item. + */ + index: number; + }) => VNode[]; + /** + * Custom controls start template. + */ + controlsstart: () => VNode[]; + /** + * Custom controls end template. + */ + controlsend: () => VNode[]; +} + +export declare type OrderListEmits = { + /** + * Emitted when the value changes. + * @param {*} value - New value. + */ + 'update:modelValue': (value: any[]) => void; + /** + * Emitted when the selection changes. + * @param {*} value - New value. + */ + 'update:selection': (value: any[]) => void; + /** + * Callback to invoke when the list is reordered. + * @param {OrderListReorderEvent} event - Custom reorder event. + */ + 'reorder': (event: OrderListReorderEvent) => void; + /** + * Callback to invoke when selection changes. + * @param {OrderListSelectionChangeEvent} event - Custom selection change event. + */ + 'selection-change': (event: OrderListSelectionChangeEvent) => void; +} + +declare class OrderList extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + OrderList: GlobalComponentConstructor + } +} + +/** + * + * OrderList is used to managed the order of a collection. + * + * Demos: + * + * - [OrderList](https://www.primefaces.org/primevue/showcase/#/orderlist) + * + */ +export default OrderList; diff --git a/components/orderlist/OrderList.spec.js b/components/orderlist/OrderList.spec.js new file mode 100644 index 000000000..6ca3da0da --- /dev/null +++ b/components/orderlist/OrderList.spec.js @@ -0,0 +1,140 @@ +import { mount } from '@vue/test-utils'; +import OrderList from './OrderList.vue'; + +describe('OrderList.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(OrderList, { + props: { + modelValue: [ + { + "id": "1000", + "code": "vbb124btr", + "name": "Game Controller", + "description": "Product Description", + "image": "game-controller.jpg", + "price": 99, + "category": "Electronics", + "quantity": 2, + "inventoryStatus": "LOWSTOCK", + "rating": 4 + }, + { + "id": "1001", + "code": "nvklal433", + "name": "Black Watch", + "description": "Product Description", + "image": "black-watch.jpg", + "price": 72, + "category": "Accessories", + "quantity": 61, + "inventoryStatus": "INSTOCK", + "rating": 4 + }, + { + "id": "1002", + "code": "zz21cz3c1", + "name": "Blue Band", + "description": "Product Description", + "image": "blue-band.jpg", + "price": 79, + "category": "Fitness", + "quantity": 2, + "inventoryStatus": "LOWSTOCK", + "rating": 3 + }, + { + "id": "1003", + "code": "244wgerg2", + "name": "Blue T-Shirt", + "description": "Product Description", + "image": "blue-t-shirt.jpg", + "price": 29, + "category": "Clothing", + "quantity": 25, + "inventoryStatus": "INSTOCK", + "rating": 5 + }, + { + "id": "1004", + "code": "h456wer53", + "name": "Bracelet", + "description": "Product Description", + "image": "bracelet.jpg", + "price": 15, + "category": "Accessories", + "quantity": 73, + "inventoryStatus": "INSTOCK", + "rating": 4 + }, + { + "id": "1005", + "code": "cm230f032", + "name": "Gaming Set", + "description": "Product Description", + "image": "gaming-set.jpg", + "price": 299, + "category": "Electronics", + "quantity": 63, + "inventoryStatus": "INSTOCK", + "rating": 3 + } + ] + }, + slots: { + header: 'List of Products', + item: ` + + ` + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-orderlist.p-component').exists()).toBe(true); + expect(wrapper.find('.p-orderlist-controls').exists()).toBe(true); + expect(wrapper.findAll('li.p-orderlist-item').length).toBe(6); + }); + + it('should select item', async () => { + await wrapper.vm.onItemClick({}, wrapper.vm.modelValue[0], 0); + + expect(wrapper.emitted()['update:selection'][0]).toEqual([[wrapper.vm.modelValue[0]]]); + + await wrapper.setProps({ selection: [wrapper.vm.modelValue[0]] }); + + expect(wrapper.findAll('li.p-orderlist-item')[0].classes()).toContain('p-highlight'); + }); + + it('should slot works', () => { + expect(wrapper.find('.p-orderlist-header').text()).toBe('List of Products'); + expect(wrapper.findAll('.product-item').length).toBe(6); + }); + + it('should change order', async () => { + await wrapper.setProps({ selection: [wrapper.vm.modelValue[2]] }); + await wrapper.setData({ d_selection: [wrapper.vm.modelValue[2]] }); + + expect(wrapper.findAll('li.p-orderlist-item')[2].classes()).toContain('p-highlight'); + + await wrapper.vm.moveUp({}); + + expect(wrapper.emitted()['update:modelValue'][0][0][1]).toEqual(wrapper.vm.modelValue[2]); + }); +}); \ No newline at end of file diff --git a/components/orderlist/OrderList.vue b/components/orderlist/OrderList.vue new file mode 100755 index 000000000..4e50e7075 --- /dev/null +++ b/components/orderlist/OrderList.vue @@ -0,0 +1,415 @@ + + + + + diff --git a/components/orderlist/package.json b/components/orderlist/package.json new file mode 100644 index 000000000..d54c58a7e --- /dev/null +++ b/components/orderlist/package.json @@ -0,0 +1,9 @@ +{ + "main": "./orderlist.cjs.js", + "module": "./orderlist.esm.js", + "unpkg": "./orderlist.min.js", + "types": "./OrderList.d.ts", + "browser": { + "./sfc": "./OrderList.vue" + } +} \ No newline at end of file diff --git a/components/organizationchart/OrganizationChart.d.ts b/components/organizationchart/OrganizationChart.d.ts new file mode 100755 index 000000000..0f50567eb --- /dev/null +++ b/components/organizationchart/OrganizationChart.d.ts @@ -0,0 +1,146 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type OrganizationChartSelectionModeType = 'single' | 'multiple' | undefined; + +export interface OrganizationChartNode { + /** + * Unique identifier of the node. (required) + */ + key: any; + /** + * Type of the node to match a template. + */ + type?: string; + /** + * Style class of the node content. + */ + styleClass?: string; + /** + * Data represented by the node. + */ + data?: any; + /** + * Whether node is selectable when selection is enabled. + * Default value is true. + */ + selectable?: boolean; + /** + * Whether node is collapsible when node expansion is enabled. + * Default value is true. + */ + collapsible?: boolean; + /** + * Children nodes array. + */ + children?: OrganizationChartNode[]; + /** + * Optional keys + */ + [key: string]: any; +} + +export interface OrganizationChartSelectionKeys { + /** + * Optional keys + */ + [key: string]: any; +} + +export interface OrganizationChartCollapsedKeys { + /** + * Optional keys + */ + [key: string]: any; +} + +export interface OrganizationChartProps { + /** + * Value of the component. + */ + value?: OrganizationChartNode; + /** + * A map instance of key-value pairs to represented the selected nodes. + */ + selectionKeys?: OrganizationChartSelectionKeys; + /** + * Type of the selection. + * @see OrganizationChartSelectionModeType + */ + selectionMode?: OrganizationChartSelectionModeType; + /** + * A map instance of key-value pairs to represented the collapsed nodes. + */ + collapsedKeys?: OrganizationChartCollapsedKeys; + /** + * Whether the nodes can be expanded or toggled. + */ + collapsible?: boolean; +} + +export interface OrganizationChartSlots { + /** + * Custom content template. + */ + default: (node: any) => VNode[]; + /** + * Dynamic content template. + */ + [key: string]: (node: any) => VNode[]; +} + +type OrganizationChartEmits = { + /** + * Emitted when the value changes. + * @param {*} value - New value. + */ + 'update:selectionKeys': (value: any) => void; + /** + * Emitted when the value changes. + * @param {boolean} value - New value. + */ + 'update:collapsedKeys': (value: boolean) => void; + /** + * Callback to invoke when a suggestion is selected. + * @param {OrganizationChartNode} node - Node instance. + */ + 'node-select': (node: OrganizationChartNode) => void; + /** + * Callback to invoke when a node is unselected. + * @param {OrganizationChartNode} node - Node instance. + */ + 'node-unselect': (node: OrganizationChartNode) => void; + /** + * Callback to invoke when a node is expanded. + * @param {OrganizationChartNode} node - Node instance. + */ + 'node-expand': (node: OrganizationChartNode) => void; + /** + * Callback to invoke when a node is collapsed. + * @param {OrganizationChartNode} node - Node instance. + */ + 'node-collapsed': (node: OrganizationChartNode) => void; +} + +declare class OrganizationChart extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + OrganizationChart: GlobalComponentConstructor + } +} + +/** + * + * OrganizationChart visualizes hierarchical organization data. + * + * Helper API: + * + * - OrganizationChartNode + * + * Demos: + * + * - [OrganizationChart](https://www.primefaces.org/primevue/showcase/#/organizationchart) + * + */ +export default OrganizationChart; diff --git a/components/organizationchart/OrganizationChart.spec.js b/components/organizationchart/OrganizationChart.spec.js new file mode 100644 index 000000000..69b9036c6 --- /dev/null +++ b/components/organizationchart/OrganizationChart.spec.js @@ -0,0 +1,160 @@ +import { mount } from '@vue/test-utils'; +import OrganizationChart from './OrganizationChart.vue'; + +describe('OrganizationChart.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(OrganizationChart, { + props: { + value: { + key: '0', + type: 'person', + styleClass: 'p-person', + data: {label: 'CEO', name: 'Walter White', avatar: 'walter.jpg'}, + children: [ + { + key: '0_0', + type: 'person', + styleClass: 'p-person', + data: {label: 'CFO', name:'Saul Goodman', avatar: 'saul.jpg'}, + children:[{ + key: '0_0_0', + data: {label: 'Tax'}, + selectable: false, + styleClass: 'department-cfo' + }, + { + key: '0_0_1', + data: {label: 'Legal'}, + selectable: false, + styleClass: 'department-cfo' + }], + }, + { + key: '0_1', + type: 'person', + styleClass: 'p-person', + data: {label: 'COO', name:'Mike E.', avatar: 'mike.jpg'}, + children:[{ + key: '0_1_0', + data: {label: 'Operations'}, + selectable: false, + styleClass: 'department-coo' + }] + }, + { + key: '0_2', + type: 'person', + styleClass: 'p-person', + data: {label: 'CTO', name:'Jesse Pinkman', avatar: 'jesse.jpg'}, + children:[{ + key: '0_2_0', + data: {label: 'Development'}, + selectable: false, + styleClass: 'department-cto', + children:[{ + key: '0_2_0_0', + data: {label: 'Analysis'}, + selectable: false, + styleClass: 'department-cto' + }, + { + key: '0_2_0_1', + data: {label: 'Front End'}, + selectable: false, + styleClass: 'department-cto' + }, + { + key: '0_2_0_2', + data: {label: 'Back End'}, + selectable: false, + styleClass: 'department-cto' + }] + }, + { + key: '0_2_1', + data: {label: 'QA'}, + selectable: false, + styleClass: 'department-cto' + }, + { + key: '0_2_2', + data: {label: 'R&D'}, + selectable: false, + styleClass: 'department-cto' + }] + } + ] + }, + collapsible: true, + selectionMode: 'single', + selectionKeys: {} + }, + slots: { + slots: { + default: ` + + `, + person: ` + + ` + } + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-organizationchart.p-component').exists()).toBe(true); + expect(wrapper.find('table.p-organizationchart-table').exists()).toBe(true); + expect(wrapper.findAll('.p-node-toggler-icon').length).toBe(5); + expect(wrapper.find('.p-node-toggler-icon').classes()).toContain('pi-chevron-down'); + }); + + it('should collapsed and expand', async () => { + await wrapper.vm.onNodeToggle(wrapper.vm.value); + + expect(wrapper.find('.p-node-toggler-icon').classes()).toContain('pi-chevron-up'); + expect(wrapper.emitted()['node-collapse'][0]).toEqual([wrapper.vm.value]); + expect(wrapper.emitted()['update:collapsedKeys'][0]).toEqual([{ '0': true }]); + expect(wrapper.vm.d_collapsedKeys).toEqual({ '0': true }); + + await wrapper.vm.onNodeToggle(wrapper.vm.value); + + expect(wrapper.find('.p-node-toggler-icon').classes()).toContain('pi-chevron-down'); + expect(wrapper.emitted()['node-expand'][0]).toEqual([wrapper.vm.value]); + expect(wrapper.emitted()['update:collapsedKeys'][0]).toEqual([{}]); + expect(wrapper.vm.d_collapsedKeys).toEqual({}); + }); + + it('should item select and unselect', async () => { + const contents = wrapper.findAll('.p-organizationchart-node-content'); + + await wrapper.vm.onNodeClick(wrapper.vm.value); + + expect(wrapper.emitted()['node-select'][0]).toEqual([wrapper.vm.value]); + expect(wrapper.emitted()['update:selectionKeys'][0]).toEqual([{ '0': true }]); + + await wrapper.setProps({ selectionKeys: { '0': true } }); + + expect(contents[0].classes()).toContain('p-highlight'); + + + await wrapper.vm.onNodeClick(wrapper.vm.value); + + expect(wrapper.emitted()['node-unselect'][0]).toEqual([wrapper.vm.value]); + expect(wrapper.emitted()['update:selectionKeys'][1]).toEqual([{}]); + + await wrapper.setProps({ selectionKeys: {} }); + + expect(contents[0].classes()).not.toContain('p-highlight'); + }); +}); \ No newline at end of file diff --git a/components/organizationchart/OrganizationChart.vue b/components/organizationchart/OrganizationChart.vue new file mode 100755 index 000000000..0aef50c67 --- /dev/null +++ b/components/organizationchart/OrganizationChart.vue @@ -0,0 +1,145 @@ + + + + + diff --git a/components/organizationchart/OrganizationChartNode.vue b/components/organizationchart/OrganizationChartNode.vue new file mode 100755 index 000000000..bf0dc6c81 --- /dev/null +++ b/components/organizationchart/OrganizationChartNode.vue @@ -0,0 +1,123 @@ + + + diff --git a/components/organizationchart/package.json b/components/organizationchart/package.json new file mode 100644 index 000000000..4d6fa8059 --- /dev/null +++ b/components/organizationchart/package.json @@ -0,0 +1,9 @@ +{ + "main": "./organizationchart.cjs.js", + "module": "./organizationchart.esm.js", + "unpkg": "./organizationchart.min.js", + "types": "./OrganizationChart.d.ts", + "browser": { + "./sfc": "./OrganizationChart.vue" + } +} \ No newline at end of file diff --git a/components/overlayeventbus/OverlayEventBus.js b/components/overlayeventbus/OverlayEventBus.js new file mode 100644 index 000000000..844a1b111 --- /dev/null +++ b/components/overlayeventbus/OverlayEventBus.js @@ -0,0 +1,3 @@ +import {EventBus} from 'primevue/utils'; + +export default EventBus(); \ No newline at end of file diff --git a/components/overlayeventbus/package.json b/components/overlayeventbus/package.json new file mode 100644 index 000000000..03bc81a3d --- /dev/null +++ b/components/overlayeventbus/package.json @@ -0,0 +1,5 @@ +{ + "main": "./overlayeventbus.cjs.js", + "module": "./overlayeventbus.esm.js", + "unpkg": "./overlayeventbus.min.js" + } \ No newline at end of file diff --git a/components/overlaypanel/OverlayPanel.d.ts b/components/overlaypanel/OverlayPanel.d.ts new file mode 100755 index 000000000..be7d83e4b --- /dev/null +++ b/components/overlaypanel/OverlayPanel.d.ts @@ -0,0 +1,120 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type OverlayPanelAppendToType = 'body' | 'self' | string | undefined | HTMLElement; + +export interface OverlayPanelBreakpoints { + /** + * Breakpoint for responsive mode. + * + * Example: + * + * + * + * Result: + * + * @media screen and (max-width: ${breakpoint[key]}) { + * .p-overlaypanel[attributeSelector] { + * width: ${breakpoint[value]} !important; + * } + * } + */ + [key: string]: string; +} + +export interface OverlayPanelProps { + /** + * Enables to hide the overlay when outside is clicked. + * Default value is true. + */ + dismissable?: boolean; + /** + * When enabled, displays a close icon at top right corner. + */ + showCloseIcon?: boolean; + /** + * A valid query selector or an HTMLElement to specify where the overlay gets attached. + * @see OverlayPanelAppendToType + * Default value is 'body'. + */ + appendTo?: OverlayPanelAppendToType; + /** + * Base zIndex value to use in layering. + * Default value is 0. + */ + baseZIndex?: number; + /** + * Whether to automatically manage layering. + * Default value is true. + */ + autoZIndex?: boolean; + /** + * Aria label of the close icon. + * Default value is 'close'. + */ + ariaCloseLabel?: string; + /** + * Object literal to define widths per screen size. + * @see OverlayPanelBreakpoints + */ + breakpoints?: OverlayPanelBreakpoints; +} + +export interface OverlayPanelSlots { + /** + * Custom content template. + */ + default: () => VNode[]; +} + +export declare type OverlayPanelEmits = { + /** + * Callback to invoke when the overlay is shown. + */ + 'show': () => void; + /** + * Callback to invoke when the overlay is hidden. + */ + 'hide': () => void; +} + +declare class OverlayPanel extends ClassComponent { + /** + * Toggles the visibility of the overlay. + * @param {Event} event - Browser event. + * + * @memberof OverlayPanel + */ + toggle: (event: Event) => void; + /** + * Shows the overlay. + * @param {Event} event - Browser event. + * @param {*} [target] - Optional target if event.currentTarget should not be used. + * + * @memberof OverlayPanel + */ + show: (event: Event, target?: any) => void; + /** + * Hides the overlay. + * + * @memberof OverlayPanel + */ + hide: () => void; +} + +declare module '@vue/runtime-core' { + interface GlobalComponents { + OverlayPanel: GlobalComponentConstructor + } +} + +/** + * + * OverlayPanel is a container component positioned as connected to its target. + * + * Demos: + * + * - [OverlayPanel](https://www.primefaces.org/primevue/showcase/#/overlaypanel) + * + */ +export default OverlayPanel; diff --git a/components/overlaypanel/OverlayPanel.spec.js b/components/overlaypanel/OverlayPanel.spec.js new file mode 100644 index 000000000..0161c3c17 --- /dev/null +++ b/components/overlaypanel/OverlayPanel.spec.js @@ -0,0 +1,47 @@ +import { mount } from '@vue/test-utils'; +import PrimeVue from '@/components/config/PrimeVue'; +import OverlayPanel from './OverlayPanel.vue'; + +describe('OverlayPanel.vue', () => { + let wrapper; + + beforeEach(async () => { + wrapper = mount(OverlayPanel, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true + } + }, + props: { + showCloseIcon: true, + ariaCloseLabel: 'exit' + }, + slots: { + default: 'PrimeVue' + } + }); + + await wrapper.vm.toggle({}, {}); + }); + + it('should exist', () => { + expect(wrapper.find('.p-overlaypanel.p-component').exists()).toBe(true); + expect(wrapper.find('.p-overlaypanel-content').exists()).toBe(true); + expect(wrapper.find('.p-overlaypanel-content').text()).toBe('PrimeVue'); + expect(wrapper.find('.p-overlaypanel-close').exists()).toBe(true); + expect(wrapper.find('.p-overlaypanel-close').attributes()['aria-label']).toBe('exit'); + }); + + it('should toggle itself', async () => { + await wrapper.vm.toggle({}, {}); + + expect(wrapper.find('.p-overlaypanel.p-component').exists()).toBe(false); + }); + + it('should close icon work', async () => { + await wrapper.vm.hide(); + + expect(wrapper.vm.visible).toBe(false); + }); +}); \ No newline at end of file diff --git a/components/overlaypanel/OverlayPanel.vue b/components/overlaypanel/OverlayPanel.vue new file mode 100755 index 000000000..b15c19015 --- /dev/null +++ b/components/overlaypanel/OverlayPanel.vue @@ -0,0 +1,361 @@ + + + + + diff --git a/components/overlaypanel/package.json b/components/overlaypanel/package.json new file mode 100644 index 000000000..840c1a3fd --- /dev/null +++ b/components/overlaypanel/package.json @@ -0,0 +1,9 @@ +{ + "main": "./overlaypanel.cjs.js", + "module": "./overlaypanel.esm.js", + "unpkg": "./overlaypanel.min.js", + "types": "./OverlayPanel.d.ts", + "browser": { + "./sfc": "./OverlayPanel.vue" + } +} \ No newline at end of file diff --git a/components/paginator/CurrentPageReport.vue b/components/paginator/CurrentPageReport.vue new file mode 100755 index 000000000..9b5177029 --- /dev/null +++ b/components/paginator/CurrentPageReport.vue @@ -0,0 +1,52 @@ + + diff --git a/components/paginator/FirstPageLink.vue b/components/paginator/FirstPageLink.vue new file mode 100755 index 000000000..7bed4b579 --- /dev/null +++ b/components/paginator/FirstPageLink.vue @@ -0,0 +1,23 @@ + + + diff --git a/components/paginator/JumpToPageDropdown.vue b/components/paginator/JumpToPageDropdown.vue new file mode 100644 index 000000000..a3d819086 --- /dev/null +++ b/components/paginator/JumpToPageDropdown.vue @@ -0,0 +1,36 @@ + + + diff --git a/components/paginator/JumpToPageInput.vue b/components/paginator/JumpToPageInput.vue new file mode 100644 index 000000000..579708bf8 --- /dev/null +++ b/components/paginator/JumpToPageInput.vue @@ -0,0 +1,26 @@ + + + diff --git a/components/paginator/LastPageLink.vue b/components/paginator/LastPageLink.vue new file mode 100755 index 000000000..3ff70289e --- /dev/null +++ b/components/paginator/LastPageLink.vue @@ -0,0 +1,23 @@ + + + diff --git a/components/paginator/NextPageLink.vue b/components/paginator/NextPageLink.vue new file mode 100755 index 000000000..f7a5a8338 --- /dev/null +++ b/components/paginator/NextPageLink.vue @@ -0,0 +1,23 @@ + + + diff --git a/components/paginator/PageLinks.vue b/components/paginator/PageLinks.vue new file mode 100755 index 000000000..2a0096feb --- /dev/null +++ b/components/paginator/PageLinks.vue @@ -0,0 +1,30 @@ + + diff --git a/components/paginator/Paginator.d.ts b/components/paginator/Paginator.d.ts new file mode 100755 index 000000000..8d4771e6b --- /dev/null +++ b/components/paginator/Paginator.d.ts @@ -0,0 +1,140 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface PageState { + /** + * Index of first record + */ + first: number; + /** + * Number of rows to display in new page + */ + rows: number; + /** + * New page number + */ + page: number; + /** + * Total number of pages + */ + pageCount?: number; +} + +export interface PaginatorProps { + /** + * Number of total records. + * Default value is 0. + */ + totalRecords?: number | undefined; + /** + * Data count to display per page. + * Default value is 0. + */ + rows?: number | undefined; + /** + * Zero-relative number of the first row to be displayed. + * Default value is 0. + */ + first?: number | undefined; + /** + * Number of page links to display. + * Default value is 5. + */ + pageLinkSize?: number | undefined; + /** + * Array of integer values to display inside rows per page dropdown. + */ + rowsPerPageOptions?: number[] | undefined; + /** + * Template of the paginator. It can be customized using the template property using the predefined keys, default value is 'FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown'. Here are the available elements that can be placed inside a paginator in any order. + * + * - FirstPageLink + * - PrevPageLink + * - PageLinks + * - NextPageLink + * - LastPageLink + * - RowsPerPageDropdown + * - JumpToPageDropdown + * - JumpToPageInput + * - CurrentPageReport + */ + template?: string | undefined; + /** + * Template of the current page report element. It displays information about the pagination state. Default value is ({currentPage} of {totalPages}) whereas available placeholders are the following; + * + * - {currentPage} + * - {totalPages} + * - {rows} + * - {first} + * - {last} + * - {totalRecords} + */ + currentPageReportTemplate?: string | undefined; + /** + * Whether to show the paginator even there is only one page. + * Default value is true. + */ + alwaysShow?: boolean | undefined; +} + +export interface PaginatorSlots { + /** + * Custom start template. + * @param {Object} scope - start slot's params. + */ + start: (scope: { + /** + * Current state + * @see PageState + */ + state: PageState; + }) => VNode[]; + /** + * Custom end template. + * @param {Object} scope - end slot's params. + */ + end: (scope: { + /** + * Current state + * @see PageState + */ + state: PageState; + }) => VNode[]; +} + +export declare type PaginatorEmits = { + /** + * Emitted when the first changes. + * @param {number} value - New value. + */ + 'update:first': (value: number) => void; + /** + * Emitted when the rows changes. + * @param {number} value - New value. + */ + 'update:rows': (value: number) => void; + /** + * Callback to invoke when page changes, the event object contains information about the new state. + * @param {PageState} event - New page state. + */ + 'page': (event: PageState) => void; +} + +declare class Paginator extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Paginator: GlobalComponentConstructor + } +} + +/** + * + * Paginator is a generic component to display content in paged format. + * + * Demos: + * + * - [Paginator](https://www.primefaces.org/primevue/showcase/#/paginator) + * + */ +export default Paginator; diff --git a/components/paginator/Paginator.spec.js b/components/paginator/Paginator.spec.js new file mode 100644 index 000000000..c010f9c00 --- /dev/null +++ b/components/paginator/Paginator.spec.js @@ -0,0 +1,49 @@ +import { mount } from '@vue/test-utils'; +import Paginator from './Paginator.vue'; + +describe('Paginator.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Paginator, { + props: { + rows: 10, + totalRecords: 120, + rowsPerPageOptions: [10,20,30] + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-paginator.p-component').exists()).toBe(true); + expect(wrapper.find('.p-paginator-icon.pi-angle-double-left').exists()).toBe(true); + expect(wrapper.find('.p-paginator-icon.pi-angle-right').exists()).toBe(true); + expect(wrapper.findAll('.p-paginator-page.p-paginator-element').length).toBe(5); + expect(wrapper.find('.p-dropdown.p-component').exists()).toBe(true); + expect(wrapper.find('.p-dropdown-label').text()).toBe('10'); + expect(wrapper.find('.p-paginator-first').classes()).toContain('p-disabled'); + expect(wrapper.find('.p-paginator-prev').classes()).toContain('p-disabled'); + expect(wrapper.vm.pageCount).toBe(12); + }); + + it('show jump to the end', async () => { + await wrapper.vm.changePageToLast({ preventDefault: () => {} }); + + expect(wrapper.findAll('.p-paginator-page.p-paginator-element')[4].classes()).toContain('p-highlight'); + expect(wrapper.findAll('.p-paginator-page.p-paginator-element')[4].text()).toBe('12'); + expect(wrapper.find('.p-paginator-next').classes()).toContain('p-disabled'); + expect(wrapper.find('.p-paginator-last').classes()).toContain('p-disabled'); + }); + + it('should change row count', async () => { + await wrapper.vm.onRowChange(20); + + expect(wrapper.vm.d_rows).toBe(20); + expect(wrapper.emitted()['update:rows'][0]).toEqual([20]); + expect(wrapper.vm.pageCount).toBe(6); + + await wrapper.setProps({ rows: 20 }); + + expect(wrapper.find('.p-dropdown-label').text()).toBe('20'); + }); +}); \ No newline at end of file diff --git a/components/paginator/Paginator.vue b/components/paginator/Paginator.vue new file mode 100755 index 000000000..e323b2576 --- /dev/null +++ b/components/paginator/Paginator.vue @@ -0,0 +1,252 @@ + + + + + diff --git a/components/paginator/PrevPageLink.vue b/components/paginator/PrevPageLink.vue new file mode 100755 index 000000000..93bf4aeb6 --- /dev/null +++ b/components/paginator/PrevPageLink.vue @@ -0,0 +1,23 @@ + + + diff --git a/components/paginator/RowsPerPageDropdown.vue b/components/paginator/RowsPerPageDropdown.vue new file mode 100755 index 000000000..aa32811d5 --- /dev/null +++ b/components/paginator/RowsPerPageDropdown.vue @@ -0,0 +1,38 @@ + + + diff --git a/components/paginator/package.json b/components/paginator/package.json new file mode 100644 index 000000000..bbecbde3d --- /dev/null +++ b/components/paginator/package.json @@ -0,0 +1,9 @@ +{ + "main": "./paginator.cjs.js", + "module": "./paginator.esm.js", + "unpkg": "./paginator.min.js", + "types": "./Paginator.d.ts", + "browser": { + "./sfc": "./Paginator.vue" + } +} \ No newline at end of file diff --git a/components/panel/Panel.d.ts b/components/panel/Panel.d.ts new file mode 100755 index 000000000..34120c6dd --- /dev/null +++ b/components/panel/Panel.d.ts @@ -0,0 +1,75 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface PanelToggleEvent { + /** + * Browser event. + */ + originalEvent: Event; + /** + * Collapsed state as a boolean + */ + value: boolean; +} + +export interface PanelProps { + /** + * Header text of the panel. + */ + header?: string; + /** + * Defines if content of panel can be expanded and collapsed. + */ + toggleable?: boolean; + /** + * Defines the initial state of panel content. + */ + collapsed?: boolean; +} + +export interface PanelSlots { + /** + * Custom content template. + */ + default: () => VNode[]; + /** + * Custom header template. + */ + header: () => VNode[]; + /** + * Custom icons template. + */ + icons: () => VNode[]; +} + +export declare type PanelEmits = { + /** + * Emitted when the collapsed changes. + * @param {boolean} value - New value. + */ + 'update:collapsed': (value: boolean) => void; + /** + * Callback to invoke when a tab toggle. + * @param {PanelToggleEvent} event - Custom toggle event. + */ + 'toggle': (event: PanelToggleEvent) => void; +} + +declare class Panel extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Panel: GlobalComponentConstructor + } +} + +/** + * + * Panel is a container with the optional content toggle feature. + * + * Demos: + * + * - [Panel](https://www.primefaces.org/primevue/showcase/#/panel) + * + */ +export default Panel; diff --git a/components/panel/Panel.spec.js b/components/panel/Panel.spec.js new file mode 100644 index 000000000..df58bfa7a --- /dev/null +++ b/components/panel/Panel.spec.js @@ -0,0 +1,33 @@ +import { mount } from '@vue/test-utils'; +import Panel from './Panel.vue'; + +describe('Panel.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Panel, { + props: { + header: 'PrimeVue' + }, + slots: { + default: '

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt

' + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-panel.p-component').exists()).toBe(true); + expect(wrapper.find('.p-panel-content').text()).toBe('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt'); + expect(wrapper.find('.p-panel-title').text()).toBe('PrimeVue'); + }); + + it('should be toggleable', async () => { + await wrapper.setProps({ toggleable: true }); + + expect(wrapper.find('.p-panel.p-component').classes()).toContain('p-panel-toggleable'); + + await wrapper.vm.toggle({}); + + expect(wrapper.emitted().toggle[0]).toEqual([{originalEvent: {}, value: true}]); + }); +}); \ No newline at end of file diff --git a/components/panel/Panel.vue b/components/panel/Panel.vue new file mode 100755 index 000000000..99c82ef21 --- /dev/null +++ b/components/panel/Panel.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/components/panel/package.json b/components/panel/package.json new file mode 100644 index 000000000..31adeb406 --- /dev/null +++ b/components/panel/package.json @@ -0,0 +1,9 @@ +{ + "main": "./panel.cjs.js", + "module": "./panel.esm.js", + "unpkg": "./panel.min.js", + "types": "./Panel.d.ts", + "browser": { + "./sfc": "./Panel.vue" + } +} \ No newline at end of file diff --git a/components/panelmenu/PanelMenu.d.ts b/components/panelmenu/PanelMenu.d.ts new file mode 100755 index 000000000..3f8be111f --- /dev/null +++ b/components/panelmenu/PanelMenu.d.ts @@ -0,0 +1,67 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; +import { MenuItem } from '../menuitem'; + +export interface PanelMenuExpandedKeys { + [key: string]: any; +} + +export interface PanelMenuProps { + /** + * An array of menuitems. + */ + model?: MenuItem[] | undefined; + /** + * A map of keys to represent the expansion state in controlled mode. + * @see PanelMenuExpandedKeys + */ + expandedKeys?: PanelMenuExpandedKeys; + /** + * Whether to apply 'router-link-active-exact' class if route exactly matches the item path. + */ + exact?: boolean | undefined; +} + +export interface PanelMenuSlots { + /** + * Custom content for each item. + * @param {Object} scope - item slot's params. + */ + item: (scope: { + /** + * Menuitem instance + */ + item: MenuItem; + }) => VNode[]; +} + +export declare type PanelMenuEmits = { + /** + * Emitted when the expandedKeys changes. + * @param {*} value - New value. + */ + 'update:expandedKeys': (value: any) => void; +} + +declare class PanelMenu extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + PanelMenu: GlobalComponentConstructor + } +} + +/** + * + * PanelMenu is a hybrid of Accordion and Tree components. + * + * Helper API: + * + * - [MenuItem](https://www.primefaces.org/primevue/showcase/#/menumodel) + * + * Demos: + * + * - [PanelMenu](https://www.primefaces.org/primevue/showcase/#/panelmenu) + * + */ +export default PanelMenu; diff --git a/components/panelmenu/PanelMenu.spec.js b/components/panelmenu/PanelMenu.spec.js new file mode 100644 index 000000000..19c80b039 --- /dev/null +++ b/components/panelmenu/PanelMenu.spec.js @@ -0,0 +1,85 @@ +import { mount } from '@vue/test-utils'; +import PanelMenu from './PanelMenu.vue'; + +describe('PanelMenu', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(PanelMenu, { + global: { + stubs: { + 'router-link': true + } + }, + props: { + model: [ + { + key: '2', + label: 'Users', + icon: 'pi pi-fw pi-user', + items: [ + { + key: '2_0', + label: 'New', + icon: 'pi pi-fw pi-user-plus', + }, + { + key: '2_1', + label: 'Delete', + icon: 'pi pi-fw pi-user-minus', + }, + { + key: '2_2', + label: 'Search', + icon: 'pi pi-fw pi-users', + items: [ + { + key: '2_2_0', + label: 'Filter', + icon: 'pi pi-fw pi-filter', + items: [ + { + key: '2_2_0_0', + label: 'Print', + icon: 'pi pi-fw pi-print' + } + ] + }, + { + key: '2_2_1', + icon: 'pi pi-fw pi-bars', + label: 'List' + } + ] + } + ] + } + ] + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-panelmenu.p-component').exists()).toBe(true); + expect(wrapper.findAll('.p-menuitem').length).toBe(6); + expect(wrapper.findAll('.p-toggleable-content').length).toBe(7); + expect(wrapper.findAll('.p-submenu-list').length).toBe(3); + }); + + it('should toggle', async () => { + expect(wrapper.findAll('.p-toggleable-content')[0].attributes().style).toBe('display: none;'); + + await wrapper.vm.onItemClick({}, wrapper.vm.model[0]); + + expect(wrapper.find('.p-panelmenu-header-link > .p-panelmenu-icon').classes()).toContain('pi-chevron-down'); + expect(wrapper.findAll('.p-toggleable-content')[0].attributes().style).toBe(undefined); + }); + + it('should update expandedKeys', async () => { + await wrapper.setProps({ expandedKeys: {'2': true} }); + + await wrapper.vm.onItemClick({}, wrapper.vm.model[0].items[2]); + + expect(wrapper.emitted()['update:expandedKeys'][0]).toEqual([{ '2': true, '2_2': true }]); + }); +}); \ No newline at end of file diff --git a/components/panelmenu/PanelMenu.vue b/components/panelmenu/PanelMenu.vue new file mode 100755 index 000000000..2f9a1b753 --- /dev/null +++ b/components/panelmenu/PanelMenu.vue @@ -0,0 +1,183 @@ + + + + + diff --git a/components/panelmenu/PanelMenuSub.vue b/components/panelmenu/PanelMenuSub.vue new file mode 100755 index 000000000..e0a2f6a2a --- /dev/null +++ b/components/panelmenu/PanelMenuSub.vue @@ -0,0 +1,121 @@ + + + diff --git a/components/panelmenu/package.json b/components/panelmenu/package.json new file mode 100644 index 000000000..71dbaddf7 --- /dev/null +++ b/components/panelmenu/package.json @@ -0,0 +1,9 @@ +{ + "main": "./panelmenu.cjs.js", + "module": "./panelmenu.esm.js", + "unpkg": "./panelmenu.min.js", + "types": "./PanelMenu.d.ts", + "browser": { + "./sfc": "./PanelMenu.vue" + } +} \ No newline at end of file diff --git a/components/password/Password.d.ts b/components/password/Password.d.ts new file mode 100755 index 000000000..0ea51a640 --- /dev/null +++ b/components/password/Password.d.ts @@ -0,0 +1,152 @@ +import { VNode, InputHTMLAttributes, HTMLAttributes } from 'vue'; +import { ClassComponent, GlobalComponentConstructor, Nullable } from '../ts-helpers'; + +type PasswordAppendToType = 'body' | 'self' | string | undefined | HTMLElement; + +export interface PasswordProps extends InputHTMLAttributes { + /** + * Value of the component. + */ + modelValue?: Nullable; + /** + * Text to prompt password entry. Defaults to PrimeVue Locale configuration. + */ + promptLabel?: string | undefined; + /** + * Regex for a medium level password. + * Default value is '^(((?=.*[a-z])(?=.*[A-Z]))|((?=.*[a-z])(?=.*[0-9]))|((?=.*[A-Z])(?=.*[0-9])))(?=.{6,})'. + */ + mediumRegex?: string | undefined; + /** + * Regex for a strong level password. + * Default value is '^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{8,})'. + */ + strongRegex?: string | undefined; + /** + * Text for a weak password. Defaults to PrimeVue Locale configuration. + */ + weakLabel?: string | undefined; + /** + * Text for a medium password. Defaults to PrimeVue Locale configuration. + */ + mediumLabel?: string | undefined; + /** + * Text for a strong password. Defaults to PrimeVue Locale configuration. + */ + strongLabel?: string | undefined; + /** + * Whether to show the strength indicator or not. + * Default value is true. + */ + feedback?: boolean | undefined; + /** + * A valid query selector or an HTMLElement to specify where the overlay gets attached. Special keywords are 'body' for document body and 'self' for the element itself. + * @see PasswordAppendToType + * Default value is 'body'. + */ + appendTo?: PasswordAppendToType; + /** + * Whether to show an icon to display the password as plain text. + */ + toggleMask?: boolean | undefined; + /** + * Icon to hide displaying the password as plain text. + * Default value is 'pi pi-eye-slash'. + */ + hideIcon?: string | undefined; + /** + * Icon to show displaying the password as plain text. + * Default value is 'pi pi-eye'. + */ + showIcon?: string | undefined; + /** + * When present, it specifies that the component should be disabled. + */ + disabled?: boolean | undefined; + /** + * Placeholder text for the input. + */ + placeholder?: string | undefined; + /** + * Identifier of the underlying input element. + */ + inputId?: string | undefined; + /** + * Inline style of the input field. + */ + inputStyle?: any | undefined; + /** + * Style class of the input field. + */ + inputClass?: any | undefined; + /** + * Uses to pass all properties of the HTMLInputElement to the focusable input element inside the component. + */ + inputProps?: InputHTMLAttributes | undefined; + /** + * Identifier of the underlying overlay panel element. + */ + panelId?: string | undefined; + /** + * Style class of the overlay panel. + */ + panelClass?: any | undefined; + /** + * Inline style of the overlay panel. + */ + panelStyle?: any | undefined; + /** + * Uses to pass all properties of the HTMLDivElement to the overlay panel inside the component. + */ + panelProps?: HTMLAttributes | undefined; + /** + * Establishes relationships between the component and label(s) where its value should be one or more element IDs. + */ + 'aria-labelledby'?: string | undefined; + /** + * Establishes a string value that labels the component. + */ + 'aria-label'?: string | undefined; +} + +export interface PasswordSlots { + /** + * Custom header template. + */ + header: () => VNode[]; + /** + * Custom footer template. + */ + footer: () => VNode[]; + /** + * Custom content template. + */ + content: () => VNode[]; +} + +export declare type PasswordEmits = { + /** + * Emitted when the value changes. + * @param {string} value - New value. + */ + 'update:modelValue': (value: string) => void; +} + +declare class Password extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Password: GlobalComponentConstructor + } +} + +/** + * + * Password displays strength indicator for password fields. + * + * Demos: + * + * - [Password](https://www.primefaces.org/primevue/showcase/#/password) + * + */ +export default Password; diff --git a/components/password/Password.spec.js b/components/password/Password.spec.js new file mode 100644 index 000000000..6e109e0d6 --- /dev/null +++ b/components/password/Password.spec.js @@ -0,0 +1,58 @@ +import { mount } from '@vue/test-utils'; +import PrimeVue from '@/components/config/PrimeVue'; +import Password from './Password.vue'; + +describe('Password.vue', () => { + let wrapper; + const event = { target: {value: 'P'}}; + + beforeEach(async () => { + wrapper = mount(Password, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true + } + }, + props: { + modelValue: null + } + }); + + await wrapper.vm.onFocus(); + }); + + it('should exist', () => { + expect(wrapper.find('.p-password.p-component').exists()).toBe(true); + expect(wrapper.find('.p-password-panel').exists()).toBe(true); + }); + + it('should update modelValue', async () => { + await wrapper.vm.onInput(event); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual(['P']); + }); + + it('should meter update', async () => { + expect(wrapper.find('.p-password-info').text()).toBe('Enter a password'); + + await wrapper.vm.onKeyUp(event); + + expect(wrapper.find('.p-password-info').text()).toBe('Weak'); + + expect(wrapper.find('.p-password-strength').classes()).toContain('weak'); + }); + + it('should toggle mask', async () => { + await wrapper.setProps({ toggleMask: true }); + + expect(wrapper.find('.p-password-input').attributes().type).toBe('password'); + expect(wrapper.find('.pi.pi-eye').exists()).toBe(true); + + await wrapper.vm.onMaskToggle(); + + expect(wrapper.find('.p-password-input').attributes().type).toBe('text'); + expect(wrapper.find('.pi.pi-eye').exists()).toBe(false); + expect(wrapper.find('.pi.pi-eye-slash').exists()).toBe(true); + }); +}); \ No newline at end of file diff --git a/components/password/Password.vue b/components/password/Password.vue new file mode 100755 index 000000000..a00c5df42 --- /dev/null +++ b/components/password/Password.vue @@ -0,0 +1,372 @@ + + + + + diff --git a/components/password/package.json b/components/password/package.json new file mode 100644 index 000000000..e73129ef8 --- /dev/null +++ b/components/password/package.json @@ -0,0 +1,9 @@ +{ + "main": "./password.cjs.js", + "module": "./password.esm.js", + "unpkg": "./password.min.js", + "types": "./Password.d.ts", + "browser": { + "./sfc": "./Password.vue" + } +} \ No newline at end of file diff --git a/components/picklist/PickList.d.ts b/components/picklist/PickList.d.ts new file mode 100755 index 000000000..9abf67f16 --- /dev/null +++ b/components/picklist/PickList.d.ts @@ -0,0 +1,221 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface PickListReorderEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Ordered list + */ + value: any[]; + /** + * Direction of the change; 'up', 'down', 'bottom', 'top' + */ + direction: string; + /** + * Index of the list that is ordered, 0 represents the source and 1 represents the target list. + */ + listIndex: number; +} + +export interface PickListSelectionChangeEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Selected item + */ + value: any[]; +} + +export interface PickListMoveToTargetEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Moved items + */ + items: any[]; +} + +/** + * @extends PickListMoveToTargetEvent + */ +export interface PickListMoveAllToTargetEvent extends PickListMoveToTargetEvent { } + +/** + * @extends PickListMoveToTargetEvent + */ +export interface PickListMoveToSourceEvent extends PickListMoveToTargetEvent { } + +/** + * @extends PickListMoveToTargetEvent + */ +export interface PickListMoveAllToSourceEvent extends PickListMoveToTargetEvent { } + +export interface PickListProps { + /** + * Value of the component as a multidimensional array. + */ + modelValue?: any[][] | undefined; + /** + * Selected items in the list as a multidimensional array. + */ + selection?: any[][] | undefined; + /** + * Name of the field that uniquely identifies the a record in the data. + */ + dataKey?: string | undefined; + /** + * Defines whether metaKey is requred or not for the selection. + * When true metaKey needs to be pressed to select or unselect an item and + * when set to false selection of each item can be toggled individually. On touch enabled devices, metaKeySelection is turned off automatically. + * Default value is true. + */ + metaKeySelection?: boolean | undefined; + /** + * Inline style of the the list element. + */ + listStyle?: any | undefined; + /** + * Whether the list optimizes layout based on screen size. + * Default value is true. + */ + responsive?: boolean | undefined; + /** + * The breakpoint to define the maximum width boundary when responsiveness is enabled. + * Default value is '960px'. + */ + breakpoint?: string | undefined; + /** + * Whether to displays rows with alternating colors. + */ + stripedRows?: boolean | undefined; + /** + * Whether to show buttons of source list. + */ + showSourceControls?: boolean | undefined; + /** + * Whether to show buttons of target list. + */ + showTargetControls?: boolean | undefined; +} + +export interface PickListSlots { + /** + * Custom header template. + */ + header: () => VNode[]; + /** + * Custom item template. + * @param {Object} scope - item slot's params. + */ + item: (scope: { + /** + * Item of the component + */ + item: any; + /** + * Index of the item + */ + index: number; + }) => VNode[]; + /** + * Custom source header template. + */ + sourceheader: () => VNode[]; + /** + * Custom target header template. + */ + targetheader: () => VNode[]; + /** + * Custom source controls start template. + */ + sourcecontrolsstart: () => VNode[]; + /** + * Custom source controls end template. + */ + sourcecontrolsend: () => VNode[]; + /** + * Custom move controls start template. + */ + movecontrolsstart: () => VNode[]; + /** + * Custom move controls end template. + */ + movecontrolsend: () => VNode[]; + /** + * Custom target controls start template. + */ + targetcontrolsstart: () => VNode[]; + /** + * Custom target controls end template. + */ + targetcontrolsend: () => VNode[]; +} + +export declare type PickListEmits = { + /** + * Emitted when the value changes. + * @param {*} value - New value. + */ + 'update:modelValue': (value: any[][]) => void; + /** + * Emitted when the selection changes. + * @param {*} value - New value. + */ + 'update:selection': (value: any[][]) => void; + /** + * Callback to invoke when the list is reordered. + * @param {PickListReorderEvent} event - Custom reorder event. + */ + 'reorder': (event: PickListReorderEvent) => void; + /** + * Callback to invoke when one or more items are moved to the other list. + * @param {PickListSelectionChangeEvent} event - Custom selection change event. + */ + 'selection-change': (event: PickListSelectionChangeEvent) => void; + /** + * Callback to invoke when one or more items are moved to the target list. + * @param {PickListMoveToTargetEvent} event - Custom move to target event. + */ + 'move-to-target': (event: PickListMoveToTargetEvent) => void; + /** + * Callback to invoke when all items are moved to the target list. + * @param {PickListMoveAllToTargetEvent} event - Custom move all to target event. + */ + 'move-all-to-target': (event: PickListMoveAllToTargetEvent) => void; + /** + * Callback to invoke when one or more items are moved to the source list. + * @param {PickListMoveToSourceEvent} event - Custom move to source event. + */ + 'move-to-source': (event: PickListMoveToSourceEvent) => void; + /** + * Callback to invoke when all items are moved to the source list. + * @param {PickListMoveAllToSourceEvent} event - Custom move all to source event. + */ + 'move-all-to-source': (event: PickListMoveAllToSourceEvent) => void; +} + +declare class PickList extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + PickList: GlobalComponentConstructor + } +} + +/** + * + * PickList is used to reorder items between different lists. + * + * Demos: + * + * - [PickList](https://www.primefaces.org/primevue/showcase/#/picklist) + * + */ +export default PickList; diff --git a/components/picklist/PickList.spec.js b/components/picklist/PickList.spec.js new file mode 100644 index 000000000..928b29f82 --- /dev/null +++ b/components/picklist/PickList.spec.js @@ -0,0 +1,169 @@ +import { mount } from '@vue/test-utils'; +import PickList from './PickList.vue'; + +describe('PickList.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(PickList, { + props: { + modelValue: [ + [ + { + "id": "1000", + "code": "vbb124btr", + "name": "Game Controller", + "description": "Product Description", + "image": "game-controller.jpg", + "price": 99, + "category": "Electronics", + "quantity": 2, + "inventoryStatus": "LOWSTOCK", + "rating": 4 + }, + { + "id": "1001", + "code": "nvklal433", + "name": "Black Watch", + "description": "Product Description", + "image": "black-watch.jpg", + "price": 72, + "category": "Accessories", + "quantity": 61, + "inventoryStatus": "INSTOCK", + "rating": 4 + } + ], + [] + ] + }, + slots: { + sourceheader: 'Available', + targetheader: 'Selected' + } + }); + }); + + + it('should exist', () => { + expect(wrapper.find('.p-picklist.p-component').exists()).toBe(true); + expect(wrapper.find('.p-picklist-list-wrapper.p-picklist-source-wrapper').exists()).toBe(true); + expect(wrapper.find('.p-picklist-list-wrapper.p-picklist-target-wrapper').exists()).toBe(true); + }); + + it('should slots work', () => { + expect(wrapper.find('.p-picklist-source-wrapper > .p-picklist-header').text()).toBe('Available'); + expect(wrapper.find('.p-picklist-target-wrapper > .p-picklist-header').text()).toBe('Selected'); + }); + + it('should update sourceList and targetList', async () => { + await wrapper.setProps({ modelValue: [ + [ + { + "id": "1000", + "code": "vbb124btr", + "name": "Game Controller", + "description": "Product Description", + "image": "game-controller.jpg", + "price": 99, + "category": "Electronics", + "quantity": 2, + "inventoryStatus": "LOWSTOCK", + "rating": 4 + } + ], + [ + { + "id": "1001", + "code": "nvklal433", + "name": "Black Watch", + "description": "Product Description", + "image": "black-watch.jpg", + "price": 72, + "category": "Accessories", + "quantity": 61, + "inventoryStatus": "INSTOCK", + "rating": 4 + } + ] + ]}); + + expect(wrapper.vm.sourceList.length).toBe(1); + expect(wrapper.vm.targetList.length).toBe(1); + }); + + it('should select an item from source list', async () => { + await wrapper.vm.onItemClick({}, wrapper.vm.modelValue[0][0], 0); + + expect(wrapper.emitted()['update:selection'][0][0]).toEqual([[wrapper.vm.modelValue[0][0]], []]) + }); + + it('should dblclick an item from source list', async () => { + await wrapper.setProps({selection: [[wrapper.vm.modelValue[0][0]], []]}); + + await wrapper.vm.onItemDblClick({}, wrapper.vm.modelValue[0][0], 0); + + expect(wrapper.emitted()['update:modelValue'][0][0][1]).toEqual([wrapper.vm.modelValue[0][0]]); + expect(wrapper.emitted()['move-to-target'][0]).toEqual([{originalEvent: {}, items: [wrapper.vm.modelValue[0][0]]}]); + expect(wrapper.emitted()['update:selection'][0][0]).toEqual([[],[]]); + }); + + it('should move item up', async () => { + await wrapper.setProps({selection: [[wrapper.vm.modelValue[0][1]], []]}); + + await wrapper.vm.moveUp({}, 0); + + expect(wrapper.emitted()['update:modelValue'][0][0]).toEqual([[{ + "id": "1001", + "code": "nvklal433", + "name": "Black Watch", + "description": "Product Description", + "image": "black-watch.jpg", + "price": 72, + "category": "Accessories", + "quantity": 61, + "inventoryStatus": "INSTOCK", + "rating": 4 + },{ + "id": "1000", + "code": "vbb124btr", + "name": "Game Controller", + "description": "Product Description", + "image": "game-controller.jpg", + "price": 99, + "category": "Electronics", + "quantity": 2, + "inventoryStatus": "LOWSTOCK", + "rating": 4 + }],[]]); + }); + + it('should should move all to target', async () => { + await wrapper.vm.moveAllToTarget({}); + + expect(wrapper.emitted()['update:modelValue'][0][0]).toEqual([[], [{ + "id": "1000", + "code": "vbb124btr", + "name": "Game Controller", + "description": "Product Description", + "image": "game-controller.jpg", + "price": 99, + "category": "Electronics", + "quantity": 2, + "inventoryStatus": "LOWSTOCK", + "rating": 4 + }, + { + "id": "1001", + "code": "nvklal433", + "name": "Black Watch", + "description": "Product Description", + "image": "black-watch.jpg", + "price": 72, + "category": "Accessories", + "quantity": 61, + "inventoryStatus": "INSTOCK", + "rating": 4 + }]]); + }); +}); diff --git a/components/picklist/PickList.vue b/components/picklist/PickList.vue new file mode 100755 index 000000000..206493873 --- /dev/null +++ b/components/picklist/PickList.vue @@ -0,0 +1,625 @@ + + + + + diff --git a/components/picklist/package.json b/components/picklist/package.json new file mode 100644 index 000000000..f0be18df0 --- /dev/null +++ b/components/picklist/package.json @@ -0,0 +1,9 @@ +{ + "main": "./picklist.cjs.js", + "module": "./picklist.esm.js", + "unpkg": "./picklist.min.js", + "types": "./PickList.d.ts", + "browser": { + "./sfc": "./PickList.vue" + } +} \ No newline at end of file diff --git a/components/portal/Portal.d.ts b/components/portal/Portal.d.ts new file mode 100644 index 000000000..97474d380 --- /dev/null +++ b/components/portal/Portal.d.ts @@ -0,0 +1,36 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type PortalAppendToType = 'body' | 'self' | string | undefined | HTMLElement; + +export interface PortalProps { + /** + * A valid query selector or an HTMLElement to specify where the dialog gets attached. Special keywords are 'body' for document body and 'self' for the element itself. + * @see PortalAppendToType + * Default value is 'body'. + */ + appendTo?: PortalAppendToType; + /** + * If disabled, the Portal feature is eliminated and the content is displayed directly. + */ + disabled?: boolean | undefined; +} + +export interface PortalSlots { + /** + * Default content slot. + */ + default: () => VNode[]; +} + +export declare type PortalEmits = { } + +declare class Portal extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Portal: GlobalComponentConstructor + } +} + +export default Portal; diff --git a/components/portal/Portal.vue b/components/portal/Portal.vue new file mode 100644 index 000000000..395bbd571 --- /dev/null +++ b/components/portal/Portal.vue @@ -0,0 +1,41 @@ + + + diff --git a/components/portal/package.json b/components/portal/package.json new file mode 100644 index 000000000..4153445b6 --- /dev/null +++ b/components/portal/package.json @@ -0,0 +1,9 @@ +{ + "main": "./portal.cjs.js", + "module": "./portal.esm.js", + "unpkg": "./portal.min.js", + "types": "./Portal.d.ts", + "browser": { + "./sfc": "./Portal.vue" + } +} diff --git a/components/progressbar/ProgressBar.d.ts b/components/progressbar/ProgressBar.d.ts new file mode 100755 index 000000000..8caa99df8 --- /dev/null +++ b/components/progressbar/ProgressBar.d.ts @@ -0,0 +1,51 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type ProgressBarModeType = 'determinate' | 'indeterminate' | undefined; + +export interface ProgressBarProps { + /** + * Current value of the progress. + */ + value?: number | undefined; + /** + * Defines the mode of the progress + * @see ProgressBarModeType + * Default value is 'determinate'. + */ + mode?: ProgressBarModeType; + /** + * Whether to display the progress bar value. + * Default value is true. + */ + showValue?: boolean | undefined; +} + +export interface ProgressBarSlots { + /** + * Custom content slot. + */ + default: () => VNode[]; +} + +export declare type ProgressBarEmits = { +} + +declare class ProgressBar extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + ProgressBar: GlobalComponentConstructor + } +} + +/** + * + * ProgressBar is a process status indicator. + * + * Demos: + * + * - [ProgressBar](https://www.primefaces.org/primevue/showcase/#/progressbar) + * + */ +export default ProgressBar; diff --git a/components/progressbar/ProgressBar.spec.js b/components/progressbar/ProgressBar.spec.js new file mode 100644 index 000000000..6825e07b9 --- /dev/null +++ b/components/progressbar/ProgressBar.spec.js @@ -0,0 +1,39 @@ +import { mount } from '@vue/test-utils'; +import ProgressBar from './ProgressBar.vue'; + +describe('ProgressBar.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(ProgressBar, { + props: { + value: 0 + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-progressbar.p-component').exists()).toBe(true); + expect(wrapper.find('.p-progressbar').attributes()['aria-valuemin']).toBe('0'); + expect(wrapper.find('.p-progressbar').attributes()['aria-valuemax']).toBe('100'); + }); + + it('should value work', async () => { + await wrapper.setProps({ value: 10 }); + + expect(wrapper.find('.p-progressbar').attributes()['aria-valuenow']).toBe('10'); + expect(wrapper.find('.p-progressbar-label').text()).toBe('10%'); + }); + + it('should not show value', async () => { + await wrapper.setProps({ showValue: false }); + + expect(wrapper.find('.p-progressbar-label').exists()).toBe(false); + }); + + it('should be indeterminated', async () => { + await wrapper.setProps({ mode: 'indeterminate'}); + + expect(wrapper.find('.p-progressbar-indeterminate-container').exists()).toBe(true); + }); +}); \ No newline at end of file diff --git a/components/progressbar/ProgressBar.vue b/components/progressbar/ProgressBar.vue new file mode 100755 index 000000000..22ded083e --- /dev/null +++ b/components/progressbar/ProgressBar.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/components/progressbar/package.json b/components/progressbar/package.json new file mode 100644 index 000000000..dc14acf10 --- /dev/null +++ b/components/progressbar/package.json @@ -0,0 +1,9 @@ +{ + "main": "./progressbar.cjs.js", + "module": "./progressbar.esm.js", + "unpkg": "./progressbar.min.js", + "types": "./ProgressBar.d.ts", + "browser": { + "./sfc": "./ProgressBar.vue" + } +} \ No newline at end of file diff --git a/components/progressspinner/ProgressSpinner.d.ts b/components/progressspinner/ProgressSpinner.d.ts new file mode 100755 index 000000000..33619dead --- /dev/null +++ b/components/progressspinner/ProgressSpinner.d.ts @@ -0,0 +1,44 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface ProgressSpinnerProps { + /** + * Width of the circle stroke. + * Default value is 2. + */ + strokeWidth?: string | undefined; + /** + * Color for the background of the circle. + */ + fill?: string | undefined; + /** + * Duration of the rotate animation. + * Default value is 2s. + */ + animationDuration?: string | undefined; +} + +export interface ProgressSpinnerSlots { +} + +export declare type ProgressSpinnerEmits = { +} + +declare class ProgressSpinner extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + ProgressSpinner: GlobalComponentConstructor + } +} + +/** + * + * ProgressSpinner is a process status indicator. + * + * Demos: + * + * - [ProgressSpinner](https://www.primefaces.org/primevue/showcase/#/progressspinner) + * + */ +export default ProgressSpinner; diff --git a/components/progressspinner/ProgressSpinner.spec.js b/components/progressspinner/ProgressSpinner.spec.js new file mode 100644 index 000000000..d1824aef6 --- /dev/null +++ b/components/progressspinner/ProgressSpinner.spec.js @@ -0,0 +1,24 @@ +import { mount } from '@vue/test-utils'; +import ProgressSpinner from './ProgressSpinner.vue'; + +describe('ProgressSpinner.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(ProgressSpinner, { + props: { + strokeWidth: '8', + fill: 'green', + animationDuration: '.5s' + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-progress-spinner').exists()).toBe(true); + expect(wrapper.find('svg.p-progress-spinner-svg').exists()).toBe(true); + expect(wrapper.find('svg.p-progress-spinner-svg').attributes().style).toBe('animation-duration: .5s;'); + expect(wrapper.find('circle.p-progress-spinner-circle').attributes().fill).toBe('green'); + expect(wrapper.find('circle.p-progress-spinner-circle').attributes().strokeMiterlimit).toBe('10'); + }); +}); \ No newline at end of file diff --git a/components/progressspinner/ProgressSpinner.vue b/components/progressspinner/ProgressSpinner.vue new file mode 100755 index 000000000..d79a2a2ef --- /dev/null +++ b/components/progressspinner/ProgressSpinner.vue @@ -0,0 +1,109 @@ + + + + + diff --git a/components/progressspinner/package.json b/components/progressspinner/package.json new file mode 100644 index 000000000..ffbe12c8d --- /dev/null +++ b/components/progressspinner/package.json @@ -0,0 +1,9 @@ +{ + "main": "./progressspinner.cjs.js", + "module": "./progressspinner.esm.js", + "unpkg": "./progressspinner.min.js", + "types": "./ProgressSpinner.d.ts", + "browser": { + "./sfc": "./ProgressSpinner.vue" + } +} \ No newline at end of file diff --git a/components/radiobutton/RadioButton.css b/components/radiobutton/RadioButton.css new file mode 100755 index 000000000..7125d8cdb --- /dev/null +++ b/components/radiobutton/RadioButton.css @@ -0,0 +1,29 @@ +.p-radiobutton { + display: inline-flex; + cursor: pointer; + user-select: none; + vertical-align: bottom; +} + +.p-radiobutton.p-radiobutton-disabled { + cursor: default; +} + +.p-radiobutton-box { + display: flex; + justify-content: center; + align-items: center; +} + +.p-radiobutton-icon { + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + transform: translateZ(0) scale(.1); + border-radius: 50%; + visibility: hidden; +} + +.p-radiobutton-box.p-highlight .p-radiobutton-icon { + transform: translateZ(0) scale(1.0, 1.0); + visibility: visible; +} \ No newline at end of file diff --git a/components/radiobutton/RadioButton.d.ts b/components/radiobutton/RadioButton.d.ts new file mode 100755 index 000000000..ba613d01e --- /dev/null +++ b/components/radiobutton/RadioButton.d.ts @@ -0,0 +1,85 @@ +import { InputHTMLAttributes } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface RadioButtonProps { + /** + * Value of the checkbox. + */ + value?: any; + /** + * Value binding of the checkbox. + */ + modelValue?: any; + /** + * Name of the input element. + */ + name?: string | undefined; + /** + * When present, it specifies that the component should be disabled. + */ + disabled?: boolean | undefined; + /** + * Identifier of the underlying input element. + */ + inputId?: string | undefined; + /** + * Inline style of the input field. + */ + inputStyle?: any; + /** + * Style class of the input field. + */ + inputClass?: any; + /** + * Uses to pass all properties of the HTMLInputElement to the focusable input element inside the component. + */ + inputProps?: InputHTMLAttributes | undefined; + /** + * Establishes relationships between the component and label(s) where its value should be one or more element IDs. + */ + 'aria-labelledby'?: string | undefined; + /** + * Establishes a string value that labels the component. + */ + 'aria-label'?: string | undefined; +} + +export interface RadioButtonSlots { +} + +export declare type RadioButtonEmits = { + /** + * Emitted when the value changes. + * @param {*} value - New value. + */ + 'update:modelValue': (value: any) => void; + /** + * Callback to invoke on radio button click. + * @param {Event} event - Browser event. + */ + 'click': (event: Event) => void; + /** + * Callback to invoke on radio button value change. + * @param {Event} event - Browser event. + */ + 'change': (event: Event) => void; +} + +declare class RadioButton extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + RadioButton: GlobalComponentConstructor + } +} + +/** + * + * RadioButton is an extension to standard radio button element with theming. + * + * Demos: + * + * - [RadioButton](https://www.primefaces.org/primevue/showcase/#/radiobutton) + * + */ +export default RadioButton; diff --git a/components/radiobutton/RadioButton.spec.js b/components/radiobutton/RadioButton.spec.js new file mode 100644 index 000000000..c85106696 --- /dev/null +++ b/components/radiobutton/RadioButton.spec.js @@ -0,0 +1,34 @@ +import { mount } from '@vue/test-utils'; +import RadioButton from './RadioButton.vue'; + +describe('RadioButton.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(RadioButton, { + props: { + value: 'Tatooine', + modelValue: null + } + }); + }); + + it('shoukd exist', () => { + expect(wrapper.find('.p-radiobutton.p-component').exists()).toBe(true); + expect(wrapper.find('input').attributes().type).toBe('radio'); + }); + + it('should clicked', async () => { + await wrapper.vm.onClick({}); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual(['Tatooine']); + expect(wrapper.emitted().change[0]).toEqual([{}]); + }); + + it('should checked', async () => { + await wrapper.setProps({ modelValue: 'Tatooine'}); + + expect(wrapper.vm.checked).toBe(true); + expect(wrapper.find('.p-radiobutton').classes()).toContain('p-radiobutton-checked'); + }); +}); \ No newline at end of file diff --git a/components/radiobutton/RadioButton.vue b/components/radiobutton/RadioButton.vue new file mode 100755 index 000000000..dddf5a4db --- /dev/null +++ b/components/radiobutton/RadioButton.vue @@ -0,0 +1,83 @@ + + + diff --git a/components/radiobutton/package.json b/components/radiobutton/package.json new file mode 100644 index 000000000..764fb372d --- /dev/null +++ b/components/radiobutton/package.json @@ -0,0 +1,9 @@ +{ + "main": "./radiobutton.cjs.js", + "module": "./radiobutton.esm.js", + "unpkg": "./radiobutton.min.js", + "types": "./RadioButton.d.ts", + "browser": { + "./sfc": "./RadioButton.vue" + } +} \ No newline at end of file diff --git a/components/rating/Rating.d.ts b/components/rating/Rating.d.ts new file mode 100755 index 000000000..46fccd4eb --- /dev/null +++ b/components/rating/Rating.d.ts @@ -0,0 +1,76 @@ +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface RatingChangeEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Selected option value + */ + value: number; +} + +export interface RatingProps { + /** + * Value of the rating. + */ + modelValue?: number | undefined; + /** + * Name of the element. + */ + name?: string | undefined; + /** + * When present, it specifies that the element should be disabled. + */ + disabled?: boolean | undefined; + /** + * When present, it specifies that component is read-only. + */ + readonly?: boolean | undefined; + /** + * Number of stars. + * Default value is 5. + */ + stars?: number | undefined; + /** + * When specified a cancel icon is displayed to allow clearing the value. + * Default value is true. + */ + cancel?: boolean | undefined; +} + +export interface RatingSlots { +} + +export declare type RatingEmits = { + /** + * Emitted when the value changes. + * @param {number} value - New value. + */ + 'update:modelValue': (value: number) => void; + /** + * Callback to invoke when a suggestion is selected. + * @param {RatingChangeEvent} event - Custom change event. + */ + 'change': (event: RatingChangeEvent) => void; +} + +declare class Rating extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Rating: GlobalComponentConstructor + } +} + +/** + * + * Rating component is a star based selection input. + * + * Demos: + * + * - [Rating](https://www.primefaces.org/primevue/showcase/#/rating) + * + */ +export default Rating; diff --git a/components/rating/Rating.spec.js b/components/rating/Rating.spec.js new file mode 100644 index 000000000..d2b8a4586 --- /dev/null +++ b/components/rating/Rating.spec.js @@ -0,0 +1,45 @@ +import { mount } from '@vue/test-utils'; +import Rating from './Rating.vue'; + +describe('Rating.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Rating); + }); + + it('should exist', () => { + expect(wrapper.find('.p-rating').exists()).toBe(true); + expect(wrapper.find('.p-rating-icon').exists()).toBe(true); + }); + + it('should update model', async () => { + await wrapper.vm.updateModel({}, 5); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual([5]); + expect(wrapper.emitted()['change'][0]).toEqual([{originalEvent: {}, value: 5}]); + }); + + it('should click', async () => { + await wrapper.vm.onStarClick({}, 1); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual([1]); + + await wrapper.setProps({ modelValue: 1 }); + + expect(wrapper.findAll('.p-rating-icon')[1].classes()).toContain('pi-star-fill'); + expect(wrapper.findAll('.p-rating-icon')[2].classes()).not.toContain('pi-star-fill'); + }); + + it('should cancel', async () => { + await wrapper.vm.onCancelClick({}); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual([null]); + }); + + it('should not cancel', async () => { + await wrapper.setProps({ cancel: false }); + + expect(wrapper.find('.p-rating-cancel').exists()).toBe(false); + }); +}); \ No newline at end of file diff --git a/components/rating/Rating.vue b/components/rating/Rating.vue new file mode 100755 index 000000000..871b190ef --- /dev/null +++ b/components/rating/Rating.vue @@ -0,0 +1,133 @@ + + + + + diff --git a/components/rating/package.json b/components/rating/package.json new file mode 100644 index 000000000..f766bcb7f --- /dev/null +++ b/components/rating/package.json @@ -0,0 +1,9 @@ +{ + "main": "./rating.cjs.js", + "module": "./rating.esm.js", + "unpkg": "./rating.min.js", + "types": "./Rating.d.ts", + "browser": { + "./sfc": "./Rating.vue" + } +} \ No newline at end of file diff --git a/components/ripple/Ripple.css b/components/ripple/Ripple.css new file mode 100644 index 000000000..541b14143 --- /dev/null +++ b/components/ripple/Ripple.css @@ -0,0 +1,28 @@ +.p-ripple { + overflow: hidden; + position: relative; +} + +.p-ink { + display: block; + position: absolute; + background: rgba(255, 255, 255, 0.5); + border-radius: 100%; + transform: scale(0); + pointer-events: none; +} + +.p-ink-active { + animation: ripple 0.4s linear; +} + +.p-ripple-disabled .p-ink { + display: none !important; +} + +@keyframes ripple { + 100% { + opacity: 0; + transform: scale(2.5); + } +} diff --git a/components/ripple/Ripple.d.ts b/components/ripple/Ripple.d.ts new file mode 100644 index 000000000..3a7786254 --- /dev/null +++ b/components/ripple/Ripple.d.ts @@ -0,0 +1,5 @@ +import { ObjectDirective } from 'vue'; + +declare const Ripple: ObjectDirective; + +export default Ripple; diff --git a/components/ripple/Ripple.js b/components/ripple/Ripple.js new file mode 100644 index 000000000..0a582a079 --- /dev/null +++ b/components/ripple/Ripple.js @@ -0,0 +1,77 @@ +import {DomHandler} from 'primevue/utils'; + +function bindEvents(el) { + el.addEventListener('mousedown', onMouseDown); +} + +function unbindEvents(el) { + el.removeEventListener('mousedown', onMouseDown); +} + +function create(el) { + let ink = document.createElement('span'); + ink.className = 'p-ink'; + ink.setAttribute("role", "presentation"); + el.appendChild(ink); + + ink.addEventListener('animationend', onAnimationEnd); +} + +function remove(el) { + let ink = getInk(el); + if (ink) { + unbindEvents(el); + ink.removeEventListener('animationend', onAnimationEnd); + ink.remove(); + } +} + +function onMouseDown(event) { + let target = event.currentTarget; + let ink = getInk(target); + if (!ink || getComputedStyle(ink, null).display === 'none') { + return; + } + + DomHandler.removeClass(ink, 'p-ink-active'); + if (!DomHandler.getHeight(ink) && !DomHandler.getWidth(ink)) { + let d = Math.max(DomHandler.getOuterWidth(target), DomHandler.getOuterHeight(target)); + ink.style.height = d + 'px'; + ink.style.width = d + 'px'; + } + + let offset = DomHandler.getOffset(target); + let x = event.pageX - offset.left + document.body.scrollTop - DomHandler.getWidth(ink) / 2; + let y = event.pageY - offset.top + document.body.scrollLeft - DomHandler.getHeight(ink) / 2; + + ink.style.top = y + 'px'; + ink.style.left = x + 'px'; + DomHandler.addClass(ink, 'p-ink-active'); +} + +function onAnimationEnd(event) { + DomHandler.removeClass(event.currentTarget, 'p-ink-active'); +} + +function getInk(el) { + for (let i = 0; i < el.children.length; i++) { + if (typeof el.children[i].className === 'string' && el.children[i].className.indexOf('p-ink') !== -1) { + return el.children[i]; + } + } + return null; +} + +const Ripple = { + mounted(el, binding) { + if (binding.instance.$primevue && binding.instance.$primevue.config && binding.instance.$primevue.config.ripple) { + create(el); + bindEvents(el); + } + }, + unmounted(el) { + remove(el); + } +}; + +export default Ripple; \ No newline at end of file diff --git a/components/ripple/Ripple.spec.js b/components/ripple/Ripple.spec.js new file mode 100644 index 000000000..f3a9b220a --- /dev/null +++ b/components/ripple/Ripple.spec.js @@ -0,0 +1,32 @@ +import { config, mount } from '@vue/test-utils'; +import Ripple from './Ripple'; + +config.global.mocks = { + $primevue: { + config: { + ripple: true + } + } +} + +config.global.directives = { + Ripple +} + +describe('Ripple', () => { + it('should exist', async () => { + const wrapper = mount({ + template: ` +
Default
+ ` + }); + + const card = wrapper.find('.card'); + + expect(wrapper.find('.p-ink').exists()).toBe(true); + + await card.trigger('mousedown'); + + expect(wrapper.find('.p-ink').classes()).toContain('p-ink-active'); + }); +}); \ No newline at end of file diff --git a/components/ripple/package.json b/components/ripple/package.json new file mode 100644 index 000000000..ee0f58f70 --- /dev/null +++ b/components/ripple/package.json @@ -0,0 +1,6 @@ +{ + "main": "./ripple.cjs.js", + "module": "./ripple.esm.js", + "unpkg": "./ripple.min.js", + "types": "./Ripple.d.ts" +} \ No newline at end of file diff --git a/components/row/Row.d.ts b/components/row/Row.d.ts new file mode 100755 index 000000000..e087072d4 --- /dev/null +++ b/components/row/Row.d.ts @@ -0,0 +1,29 @@ +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface RowProps { +} + +export interface RowSlots { +} + +export declare type RowEmits = { +} + +declare class Row extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Row: GlobalComponentConstructor + } +} + +/** + * + * Row is a helper component to create column group. + * + * Demos: + * + * - [DataTable](https://www.primefaces.org/primevue/showcase/#/datatable/colgroup) + * + */ +export default Row; diff --git a/components/row/Row.vue b/components/row/Row.vue new file mode 100755 index 000000000..2d9a1a917 --- /dev/null +++ b/components/row/Row.vue @@ -0,0 +1,8 @@ + diff --git a/components/row/package.json b/components/row/package.json new file mode 100644 index 000000000..9361fc690 --- /dev/null +++ b/components/row/package.json @@ -0,0 +1,9 @@ +{ + "main": "./row.cjs.js", + "module": "./row.esm.js", + "unpkg": "./row.min.js", + "types": "./Row.d.ts", + "browser": { + "./sfc": "./Row.vue" + } +} \ No newline at end of file diff --git a/components/scrollpanel/ScrollPanel.d.ts b/components/scrollpanel/ScrollPanel.d.ts new file mode 100644 index 000000000..8e532ca90 --- /dev/null +++ b/components/scrollpanel/ScrollPanel.d.ts @@ -0,0 +1,34 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface ScrollPanelProps { +} + +export interface ScrollPanelSlots { + /** + * Custom content slot. + */ + default: () => VNode[]; +} + +export declare type ScrollPanelEmits = { +} + +declare class ScrollPanel extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + ScrollPanel: GlobalComponentConstructor + } +} + +/** + * + * ScrollPanel is a cross browser, lightweight and themable alternative to native browser scrollbar. + * + * Demos: + * + * - [ScrollPanel](https://www.primefaces.org/primevue/showcase/#/scrollpanel) + * + */ +export default ScrollPanel; diff --git a/components/scrollpanel/ScrollPanel.spec.js b/components/scrollpanel/ScrollPanel.spec.js new file mode 100644 index 000000000..b8f174a18 --- /dev/null +++ b/components/scrollpanel/ScrollPanel.spec.js @@ -0,0 +1,51 @@ +import { shallowMount } from '@vue/test-utils'; +import ScrollPanel from './ScrollPanel.vue'; + +describe('ScrollPanel.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = shallowMount(ScrollPanel, { + slots: { + default: ` +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Vitae et leo duis ut diam. + Ultricies mi quis hendrerit dolor magna eget est lorem. Amet consectetur adipiscing elit ut. + Nam libero justo laoreet sit amet. Pharetra massa massa ultricies mi quis hendrerit dolor magna. + Est ultricies integer quis auctor elit sed vulputate. Consequat ac felis donec et. Tellus orci ac auctor augue mauris. + Semper feugiat nibh sed pulvinar proin gravida hendrerit lectus a. Tincidunt arcu non sodales neque sodales. + Metus aliquam eleifend mi in nulla posuere sollicitudin aliquam ultrices. Sodales ut etiam sit amet nisl purus. + Cursus sit amet dictum sit amet. Tristique senectus et netus et malesuada fames ac turpis egestas. + Et tortor consequat id porta nibh venenatis cras sed. Diam maecenas ultricies mi eget mauris. + Eget egestas purus viverra accumsan in nisl nisi. Suscipit adipiscing bibendum est ultricies integer. + Mattis aliquam faucibus purus in massa tempor nec. +

+ ` + } + }); + + wrapper.element.setAttribute('width', '200px'); + wrapper.element.setAttribute('height', '200px'); + }); + + it('should exist', () => { + expect(wrapper.find('.p-scrollpanel.p-component').exists()).toBe(true); + }); + + it('should scroll down y', async () => { + const event = { pageY: 100, preventDefault: () => {} }; + + await wrapper.vm.onYBarMouseDown(event); + + expect(wrapper.find('.p-scrollpanel-bar-y').classes()).toContain('p-scrollpanel-grabbed'); + }); + + it('should scroll down x', async () => { + const event = { pageX: 100, preventDefault: () => {} }; + + await wrapper.vm.onXBarMouseDown(event); + + expect(wrapper.find('.p-scrollpanel-bar-x').classes()).toContain('p-scrollpanel-grabbed'); + }); +}); \ No newline at end of file diff --git a/components/scrollpanel/ScrollPanel.vue b/components/scrollpanel/ScrollPanel.vue new file mode 100644 index 000000000..b59e0b14d --- /dev/null +++ b/components/scrollpanel/ScrollPanel.vue @@ -0,0 +1,264 @@ + + + + + diff --git a/components/scrollpanel/package.json b/components/scrollpanel/package.json new file mode 100644 index 000000000..61ee21a9d --- /dev/null +++ b/components/scrollpanel/package.json @@ -0,0 +1,9 @@ +{ + "main": "./scrollpanel.cjs.js", + "module": "./scrollpanel.esm.js", + "unpkg": "./scrollpanel.min.js", + "types": "./ScrollPanel.d.ts", + "browser": { + "./sfc": "./ScrollPanel.vue" + } +} \ No newline at end of file diff --git a/components/scrolltop/ScrollTop.d.ts b/components/scrolltop/ScrollTop.d.ts new file mode 100644 index 000000000..95dd40a4d --- /dev/null +++ b/components/scrolltop/ScrollTop.d.ts @@ -0,0 +1,52 @@ +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type ScrollTopTargetType = 'window' | 'parent' | undefined; + +export interface ScrollTopProps { + /** + * Target of the ScrollTop. + * @see ScrollTopTargetType + * Default value is 'window'. + */ + target?: ScrollTopTargetType; + /** + * Defines the threshold value of the vertical scroll position of the target to toggle the visibility. + * Default value is 400. + */ + threshold?: number | undefined; + /** + * Icon to display. + * Default value is 'pi pi-chevron-up'. + */ + icon?: string | undefined; + /** + * Defines the scrolling behaviour, 'smooth' adds an animation and 'auto' scrolls with a jump. + * Default value is 'smooth'. + */ + behavior?: string | undefined; +} + +export interface ScrollTopSlots { +} + +export declare type ScrollTopEmits = { +} + +declare class ScrollTop extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + ScrollTop: GlobalComponentConstructor + } +} + +/** + * + * ScrollTop gets displayed after a certain scroll position and used to navigates to the top of the page quickly. + * + * Demos: + * + * - [ScrollTop](https://www.primefaces.org/primevue/showcase/#/scrolltop) + * + */ +export default ScrollTop; diff --git a/components/scrolltop/ScrollTop.spec.js b/components/scrolltop/ScrollTop.spec.js new file mode 100644 index 000000000..edd5bc9d0 --- /dev/null +++ b/components/scrolltop/ScrollTop.spec.js @@ -0,0 +1,16 @@ +import { mount } from '@vue/test-utils'; +import ScrollTop from './ScrollTop.vue'; + +describe('ScrollTop.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(ScrollTop); + }); + + it('should exist', async() => { + await wrapper.setData({ visible: true }); + + expect(wrapper.find('.p-scrolltop.p-component').exists()).toBe(true); + }); +}); \ No newline at end of file diff --git a/components/scrolltop/ScrollTop.vue b/components/scrolltop/ScrollTop.vue new file mode 100644 index 000000000..ab9d8aa05 --- /dev/null +++ b/components/scrolltop/ScrollTop.vue @@ -0,0 +1,150 @@ + + + + + diff --git a/components/scrolltop/package.json b/components/scrolltop/package.json new file mode 100644 index 000000000..69d990a17 --- /dev/null +++ b/components/scrolltop/package.json @@ -0,0 +1,9 @@ +{ + "main": "./scrolltop.cjs.js", + "module": "./scrolltop.esm.js", + "unpkg": "./scrolltop.min.js", + "types": "./ScrollTop.d.ts", + "browser": { + "./sfc": "./ScrollTop.vue" + } +} \ No newline at end of file diff --git a/components/selectbutton/SelectButton.d.ts b/components/selectbutton/SelectButton.d.ts new file mode 100755 index 000000000..2e8b9c142 --- /dev/null +++ b/components/selectbutton/SelectButton.d.ts @@ -0,0 +1,121 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type SelectButtonOptionLabelType = string | ((data: any) => string) | undefined; + +type SelectButtonOptionValueType = string | ((data: any) => any) | undefined; + +type SelectButtonOptionDisabledType = string | ((data: any) => boolean) | undefined; + +export interface SelectButtonChangeEvent { + /** + * Browser event. + */ + originalEvent: Event; + /** + * Single value or an array of values that are selected. + */ + value: any; +} + +export interface SelectButtonProps { + /** + * Value of the component. + */ + modelValue?: any; + /** + * An array of selectitems to display as the available options. + */ + options?: any[] | undefined; + /** + * Property name or getter function to use as the label of an option. + */ + optionLabel?: SelectButtonOptionLabelType; + /** + * Property name or getter function to use as the value of an option, defaults to the option itself when not defined. + */ + optionValue?: SelectButtonOptionValueType; + /** + * Property name or getter function to use as the disabled flag of an option, defaults to false when not defined. + */ + optionDisabled?: SelectButtonOptionDisabledType; + /** + * When specified, allows selecting multiple values. + */ + multiple?: boolean | undefined; + /** + * When present, it specifies that the element should be disabled. + */ + disabled?: boolean | undefined; + /** + * A property to uniquely identify an option. + */ + dataKey?: string | undefined; + /** + * Whether selection can be cleared. + */ + unselectable?: boolean | undefined; + /** + * Identifier of the underlying element. + */ + "aria-labelledby"?: string | undefined; +} + +export interface SelectButtonSlots { + /** + * Custom content for each option. + * @param {Object} scope - option slot's params. + */ + option: (scope: { + /** + * Option instance + */ + option: any; + /** + * Index of the option + */ + index: number; + }) => VNode[]; +} + +export declare type SelectButtonEmits = { + /** + * Emitted when the value changes. + * @param {*} value - New value. + */ + 'update:modelValue': (value: any) => void; + /** + * Callback to invoke on value change. + * @param {SelectButtonChangeEvent} event - Custom change event. + */ + 'change': (event: SelectButtonChangeEvent) => void; + /** + * Callback to invoke on focus. + * @param {SelectButtonChangeEvent} event - Browser event. + */ + 'focus': (event: Event) => void; + /** + * Callback to invoke on blur. + * @param {Event} event - Browser event. + */ + 'blur': (event: Event) => void; +} + +declare class SelectButton extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + SelectButton: GlobalComponentConstructor + } +} + +/** + * + * SelectButton is a form component to choose a value from a list of options using button elements. + * + * Demos: + * + * - [SelectButton](https://www.primefaces.org/primevue/showcase/#/selectbutton) + * + */ +export default SelectButton; diff --git a/components/selectbutton/SelectButton.spec.js b/components/selectbutton/SelectButton.spec.js new file mode 100644 index 000000000..4a5387a0b --- /dev/null +++ b/components/selectbutton/SelectButton.spec.js @@ -0,0 +1,56 @@ +import { mount } from '@vue/test-utils'; +import SelectButton from './SelectButton.vue'; + +describe('SelectButton.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(SelectButton, { + props: { + modelValue: null, + options: ['Off', 'On'] + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-selectbutton.p-component').exists()).toBe(true); + expect(wrapper.findAll('.p-button.p-component').length).toBe(2); + }); + + it('should option select', async () => { + await wrapper.vm.onOptionSelect({}, wrapper.vm.options[0]); + + expect(wrapper.emitted()['update:modelValue'][0]).toEqual(['Off']); + + await wrapper.setProps({ modelValue: wrapper.vm.options[0] }); + + expect(wrapper.findAll('.p-button.p-component')[0].attributes()['aria-pressed']).toBe('true'); + expect(wrapper.findAll('.p-button.p-component')[0].classes()).toContain('p-highlight'); + }); +}); + +describe('multiple select', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(SelectButton, { + props: { + modelValue: null, + options: [ + {name: 'Option 1', value: 1}, + {name: 'Option 2', value: 2}, + {name: 'Option 3', value: 3} + ], + optionLabel: 'name', + multiple: true + } + }); + }); + + it('should select', async () => { + await wrapper.setProps({ modelValue: wrapper.vm.options }); + + expect(wrapper.findAll('.p-highlight').length).toBe(3); + }); +}); \ No newline at end of file diff --git a/components/selectbutton/SelectButton.vue b/components/selectbutton/SelectButton.vue new file mode 100755 index 000000000..8a095423d --- /dev/null +++ b/components/selectbutton/SelectButton.vue @@ -0,0 +1,191 @@ + + + diff --git a/components/selectbutton/package.json b/components/selectbutton/package.json new file mode 100644 index 000000000..9183b67ea --- /dev/null +++ b/components/selectbutton/package.json @@ -0,0 +1,9 @@ +{ + "main": "./selectbutton.cjs.js", + "module": "./selectbutton.esm.js", + "unpkg": "./selectbutton.min.js", + "types": "./SelectButton.d.ts", + "browser": { + "./sfc": "./SelectButton.vue" + } +} \ No newline at end of file diff --git a/components/sidebar/Sidebar.d.ts b/components/sidebar/Sidebar.d.ts new file mode 100755 index 000000000..98ab3b586 --- /dev/null +++ b/components/sidebar/Sidebar.d.ts @@ -0,0 +1,93 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type SidebarPositionType = 'left' | 'right' | 'top' | 'bottom' | 'full' | undefined; + +export interface SidebarProps { + /** + * Specifies the visibility of the dialog. + */ + visible?: boolean | undefined; + /** + * Specifies the position of the sidebar. + * @see SidebarPositionType + * Default value is 'left'. + */ + position?: SidebarPositionType; + /** + * Base zIndex value to use in layering. + * Default value is 0. + */ + baseZIndex?: number | undefined; + /** + * Whether to automatically manage layering. + * Default value is true. + */ + autoZIndex?: boolean | undefined; + /** + * Whether clicking outside closes the panel. + * Default value is true. + */ + dismissable?: boolean | undefined; + /** + * Whether to display a close icon inside the panel. + * Default value is true. + */ + showCloseIcon?: boolean | undefined; + /** + * Whether to a modal layer behind the sidebar. + * Default value is true. + */ + modal?: boolean | undefined; + /** + * Aria label of the close icon. + * Default value is 'close'. + */ + ariaCloseLabel?: string | undefined; +} + +export interface SidebarSlots { + /** + * Custom content template. + */ + default: () => VNode[]; + /** + * Custom header template. + */ + header: () => VNode[]; +} + +export declare type SidebarEmits = { + /** + * Emitted when the value changes. + * @param {boolean} value - New value. + */ + 'update:modelValue': (value: boolean) => void; + /** + * Callback to invoke when sidebar gets shown. + */ + 'show': () => void; + /** + * Callback to invoke when sidebar gets hidden. + */ + 'hide': () => void; +} + +declare class Sidebar extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Sidebar: GlobalComponentConstructor + } +} + +/** + * + * Sidebar is a panel component displayed as an overlay at the edges of the screen. + * + * Demos: + * + * - [Sidebar](https://www.primefaces.org/primevue/showcase/#/sidebar) + * + */ +export default Sidebar; diff --git a/components/sidebar/Sidebar.spec.js b/components/sidebar/Sidebar.spec.js new file mode 100644 index 000000000..eda571e6f --- /dev/null +++ b/components/sidebar/Sidebar.spec.js @@ -0,0 +1,54 @@ +import { mount } from '@vue/test-utils'; +import PrimeVue from '@/components/config/PrimeVue'; +import Sidebar from './Sidebar.vue'; + +describe('Sidebar.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Sidebar, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true + } + }, + props: { + visible: true, + bazeZIndex: 1000 + }, + slots: { + default: `

Left Sidebar

` + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-sidebar.p-component').exists()).toBe(true); + expect(wrapper.find('.p-sidebar').classes()).toContain('p-sidebar-left'); + expect(wrapper.find('.p-sidebar').classes()).toContain('p-sidebar-active'); + }); + + it('should close', async () => { + await wrapper.vm.hide(); + + expect(wrapper.emitted()['update:visible'][0]).toEqual([false]); + + await wrapper.setProps({ visible: false }); + + expect(wrapper.find('.p-sidebar.p-component').exists()).toBe(false); + }); + + it('should set position', async () => { + await wrapper.setProps({ position: 'bottom' }); + + expect(wrapper.find('.p-sidebar').classes()).toContain('p-sidebar-bottom'); + }); + + it('should set position', async () => { + await wrapper.setProps({ position: 'full' }); + + expect(wrapper.vm.fullScreen).toBe(true); + expect(wrapper.find('.p-sidebar').classes()).toContain('p-sidebar-full'); + }); +}); \ No newline at end of file diff --git a/components/sidebar/Sidebar.vue b/components/sidebar/Sidebar.vue new file mode 100755 index 000000000..3412e489f --- /dev/null +++ b/components/sidebar/Sidebar.vue @@ -0,0 +1,307 @@ + + + + + diff --git a/components/sidebar/package.json b/components/sidebar/package.json new file mode 100644 index 000000000..3890cce8a --- /dev/null +++ b/components/sidebar/package.json @@ -0,0 +1,9 @@ +{ + "main": "./sidebar.cjs.js", + "module": "./sidebar.esm.js", + "unpkg": "./sidebar.min.js", + "types": "./Sidebar.d.ts", + "browser": { + "./sfc": "./Sidebar.vue" + } +} \ No newline at end of file diff --git a/components/skeleton/Skeleton.d.ts b/components/skeleton/Skeleton.d.ts new file mode 100644 index 000000000..57274a2cf --- /dev/null +++ b/components/skeleton/Skeleton.d.ts @@ -0,0 +1,63 @@ +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type SkeletonShapeType = 'rectangle' | 'circle' | undefined; + +type SkeletonAnimationType = 'wave' | 'none' | undefined; + +export interface SkeletonProps { + /** + * Shape of the element. + * @see SkeletonShapeType + * Default value is 'rectangle'. + */ + shape?: SkeletonShapeType; + /** + * Size of the Circle or Square. + */ + size?: string | undefined; + /** + * Width of the element. + * Default value is '100%'. + */ + width?: string | undefined; + /** + * Height of the element. + * Default value is '1rem'. + */ + height?: string | undefined; + /** + * Border radius of the element, defaults to value from theme. + */ + borderRadius?: string | undefined; + /** + * Type of the animation. + * @see SkeletonAnimationType + * Default value is 'wave'. + */ + animation?: SkeletonAnimationType; +} + +export interface SkeletonSlots { +} + +export declare type SkeletonEmits = { +} + +declare class Skeleton extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Skeleton: GlobalComponentConstructor + } +} + +/** + * + * Skeleton is a placeholder to display instead of the actual content. + * + * Demos: + * + * - [Skeleton](https://www.primefaces.org/primevue/showcase/#/skeleton) + * + */ +export default Skeleton; diff --git a/components/skeleton/Skeleton.spec.js b/components/skeleton/Skeleton.spec.js new file mode 100644 index 000000000..954a3e461 --- /dev/null +++ b/components/skeleton/Skeleton.spec.js @@ -0,0 +1,38 @@ +import { mount } from '@vue/test-utils'; +import Skeleton from './Skeleton.vue'; + +describe('Skeleton.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Skeleton); + }); + + it('should exist', () => { + expect(wrapper.find('.p-skeleton.p-component').exists()).toBe(true); + }); + + it('should get width and height', async () => { + await wrapper.setProps({ width: '5rem', height: '2rem', borderRadius: '10px' }); + + expect(wrapper.find('.p-skeleton').attributes().style).toEqual('width: 5rem; height: 2rem; border-radius: 10px;'); + }); + + it('should get size', async () => { + await wrapper.setProps({ size: '4rem' }); + + expect(wrapper.find('.p-skeleton').attributes().style).toEqual('width: 4rem; height: 4rem;'); + }); + + it('should get shape', async () => { + await wrapper.setProps({ shape: 'circle' }); + + expect(wrapper.find('.p-skeleton').classes()).toContain('p-skeleton-circle'); + }); + + it('should remove animation', async () => { + await wrapper.setProps({ animation: 'none' }); + + expect(wrapper.find('.p-skeleton').classes()).toContain('p-skeleton-none'); + }); +}); diff --git a/components/skeleton/Skeleton.vue b/components/skeleton/Skeleton.vue new file mode 100644 index 000000000..b60aa2c34 --- /dev/null +++ b/components/skeleton/Skeleton.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/components/skeleton/package.json b/components/skeleton/package.json new file mode 100644 index 000000000..a81355416 --- /dev/null +++ b/components/skeleton/package.json @@ -0,0 +1,9 @@ +{ + "main": "./skeleton.cjs.js", + "module": "./skeleton.esm.js", + "unpkg": "./skeleton.min.js", + "types": "./Skeleton.d.ts", + "browser": { + "./sfc": "./Skeleton.vue" + } +} \ No newline at end of file diff --git a/components/slider/Slider.d.ts b/components/slider/Slider.d.ts new file mode 100755 index 000000000..faa672404 --- /dev/null +++ b/components/slider/Slider.d.ts @@ -0,0 +1,103 @@ +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type SliderOrientationType = 'horizontal' | 'vertical' | undefined; + +export interface SliderSlideEndEvent { + /** + * Original event + */ + originalEvent: Event; + /** + * New value. + */ + value: number; +} + +export interface SliderProps { + /** + * Value of the component. + * Default value is 0. + */ + modelValue?: number | number[] | undefined; + /** + * Mininum boundary value. + * Default value is 0. + */ + min?: number | undefined; + /** + * Maximum boundary value. + * Default value is 100. + */ + max?: number | undefined; + /** + * Orientation of the slider. + * @see SliderOrientationType + * Default value is 'horizontal'. + */ + orientation?: SliderOrientationType; + /** + * Step factor to increment/decrement the value. + * Default value is 1. + */ + step?: number | undefined; + /** + * When speficed, allows two boundary values to be picked. + */ + range?: boolean | undefined; + /** + * When present, it specifies that the component should be disabled. + */ + disabled?: boolean | undefined; + /** + * Index of the element in tabbing order. + */ + tabindex?: number | undefined; + /** + * Establishes relationships between the component and label(s) where its value should be one or more element IDs. + */ + 'aria-labelledby'?: string | undefined; + /** + * Used to define a string that labels the element. + */ + 'aria-label'?: string | undefined; +} + +export interface SliderSlots { +} + +export declare type SliderEmits = { + /** + * Emitted when the value changes. + * @param {number | number[]} value - New value. + */ + 'update:modelValue': (value: number | number[]) => void; + /** + * Callback to invoke on value change. + * @param {number} value - New value + */ + 'change': (value: number) => void; + /** + * Callback to invoke when slide ends. + * @param {SliderSlideEndEvent} event - Custom slide end event. + */ + 'slideend': (event: SliderSlideEndEvent) => void; +} + +declare class Slider extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Slider: GlobalComponentConstructor + } +} + +/** + * + * Slider is an input component to provide a numerical input. + * + * Demos: + * + * - [Slider](https://www.primefaces.org/primevue/showcase/#/slider) + * + */ +export default Slider; diff --git a/components/slider/Slider.spec.js b/components/slider/Slider.spec.js new file mode 100644 index 000000000..fdb600a4e --- /dev/null +++ b/components/slider/Slider.spec.js @@ -0,0 +1,49 @@ +import { mount } from '@vue/test-utils'; +import Slider from './Slider.vue'; + +describe('Slider.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Slider, { + props: { + modelValue: null + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-slider.p-component').exists()).toBe(true); + expect(wrapper.find('.p-slider').classes()).toContain('p-slider-horizontal'); + }); + + it('should drag start and end', async () => { + await wrapper.vm.onDragStart({ preventDefault: () => {} }); + + expect(wrapper.find('.p-slider').classes()).toContain('p-slider-sliding'); + + await wrapper.vm.onDragEnd(); + + expect(wrapper.find('.p-slider').classes()).not.toContain('p-slider-sliding'); + }); + + it('should set value', async () => { + wrapper.element.setAttribute('width', '14rem'); + + await wrapper.vm.updateDomData(); + + await wrapper.vm.setValue({ pageX: 60 }); // TODO: + + expect(wrapper.emitted()['update:modelValue'][0][0]).toBeGreaterThan(0); + }); + + it('should set value on vertical mode', async () => { + await wrapper.setProps({ orientation: 'vertical', modelValue: 0 }); + + await wrapper.vm.updateDomData(); + + await wrapper.vm.setValue({ pageY: 111 }); // TODO: + + expect(wrapper.emitted()['update:modelValue'][0][0]).toBeGreaterThan(0); + }); +}); \ No newline at end of file diff --git a/components/slider/Slider.vue b/components/slider/Slider.vue new file mode 100755 index 000000000..613d00908 --- /dev/null +++ b/components/slider/Slider.vue @@ -0,0 +1,392 @@ + + + + + diff --git a/components/slider/package.json b/components/slider/package.json new file mode 100644 index 000000000..ebbb97ca7 --- /dev/null +++ b/components/slider/package.json @@ -0,0 +1,9 @@ +{ + "main": "./slider.cjs.js", + "module": "./slider.esm.js", + "unpkg": "./slider.min.js", + "types": "./Slider.d.ts", + "browser": { + "./sfc": "./Slider.vue" + } +} \ No newline at end of file diff --git a/components/speeddial/SpeedDial.d.ts b/components/speeddial/SpeedDial.d.ts new file mode 100644 index 000000000..6f661bfed --- /dev/null +++ b/components/speeddial/SpeedDial.d.ts @@ -0,0 +1,176 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; +import { MenuItem } from '../menuitem'; + +type SpeedDialDirectionType = 'up' | 'down' | 'left' | 'right' | 'up-left' | 'up-right' | 'down-left' | 'down-right' | undefined; + +type SpeedDialType = 'linear' | 'circle' | 'semi-circle' | 'quarter-circle' | undefined; + +type SpeedDialTooltipPositionType = 'bottom' | 'top' | 'left' | 'right' | undefined; + +type SpeedDialTooltipEventType = 'hover' | 'focus' | undefined; + +export interface SpeedDialTooltipOptions { + /** + * Event to show the tooltip, valid values are hover and focus. + * @see SpeedDialTooltipEventType + */ + event: string; + /** + * Position of element. + * @see SpeedDialTooltipPositionType + * Default value is 'bottom'. + */ + position: string; + /** + * Optional options. + */ + [key: string]: string; +} + +export interface SpeedDialProps { + /** + * MenuModel instance to define the action items. + */ + model?: MenuItem[] | undefined; + /** + * Specifies the visibility of the overlay. + */ + visible?: boolean | undefined; + /** + * Specifies the opening direction of actions. + * @see SpeedDialDirectionType + * Default value is 'up'. + */ + direction?: SpeedDialDirectionType; + /** + * Transition delay step for each action item. + * Default value is 30. + */ + transitionDelay?: number | undefined; + /** + * Specifies the opening type of actions. + * @see SpeedDialType + * Default value is 'linear'. + */ + type?: SpeedDialType; + /** + * Radius for *circle types. + * Default value is 0. + */ + radius?: number | undefined; + /** + * Whether to show a mask element behind the speeddial. + */ + mask?: boolean | undefined; + /** + * Whether the component is disabled. + */ + disabled?: boolean | undefined; + /** + * Whether the actions close when clicked outside. + * Default value is true. + */ + hideOnClickOutside?: boolean | undefined; + /** + * Style class of the button element. + */ + buttonClass?: any; + /** + * Inline style of the mask element. + */ + maskStyle?: any; + /** + * Style class of the mask element. + */ + maskClass?: string | undefined; + /** + * Show icon of the button element. + * Default value is 'pi pi-plus'. + */ + showIcon?: string | undefined; + /** + * Hide icon of the button element. + */ + hideIcon?: string | undefined; + /** + * Defined to rotate showIcon when hideIcon is not present. + * Default value is true. + */ + rotateAnimation?: boolean | undefined; + /** + * Style class of the element. + */ + class?: any; + /** + * Inline style of the element. + */ + style?: any; + /** + * Whether to display the tooltip on items. The modifiers of Tooltip can be used like an object in it. Valid keys are 'event' and 'position'. + * @see SpeedDialTooltipOptions + */ + tooltipOptions?: SpeedDialTooltipOptions; +} + +export interface SpeedDialSlots { + /** + * Custom content for each item. + * @param {Object} scope - item slot's params. + */ + item: (scope: { + /** + * Menuitem instance + */ + item: MenuItem; + }) => VNode[]; + /** + * Custom button template. + * @param {Object} scope - button slot's params. + */ + button: (scope: { + /** + * Toggle metadata + */ + toggle: () => void; + }) => VNode[]; +} + +export declare type SpeedDialEmits = { + /** + * Fired when the button element clicked. + * @param {Event} event - Browser event. + */ + 'click': (event: Event) => void; + /** + * Fired when the actions are visible. + */ + 'show': () => void; + /** + * Fired when the actions are hidden. + */ + 'hide': () => void; +} + +declare class SpeedDial extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + SpeedDial: GlobalComponentConstructor + } +} + +/** + * + * When pressed, a floating action button can display multiple primary actions that can be performed on a page. + * + * Helper API: + * + * - [MenuItem](https://www.primefaces.org/primevue/showcase/#/menumodel) + * + * Demos: + * + * - [SpeedDial](https://www.primefaces.org/primevue/showcase/#/speeddial) + * + */ +export default SpeedDial; diff --git a/components/speeddial/SpeedDial.spec.js b/components/speeddial/SpeedDial.spec.js new file mode 100644 index 000000000..1d028a6d9 --- /dev/null +++ b/components/speeddial/SpeedDial.spec.js @@ -0,0 +1,97 @@ +import { mount } from '@vue/test-utils'; +import SpeedDial from './SpeedDial.vue'; + +describe('SpeedDial.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(SpeedDial, { + props: { + model: [ + { + label: 'Add', + icon: 'pi pi-pencil' + }, + { + label: 'Update', + icon: 'pi pi-refresh' + }, + { + label: 'Delete', + icon: 'pi pi-trash' + }, + { + label: 'Upload', + icon: 'pi pi-upload', + command: () => { + window.location.hash = "/fileupload" + } + }, + { + label: 'Vue Website', + icon: 'pi pi-external-link', + command: () => { + window.location.href = 'https://vuejs.org/' + } + } + ], + direction: 'down' + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-speeddial.p-component').exists()).toBe(true); + expect(wrapper.find('.p-speeddial').classes()).toContain('p-speeddial-direction-down'); + expect(wrapper.findAll('li.p-speeddial-item').length).toEqual(5); + }); + + it('should show the list', async () => { + await wrapper.vm.onClick({}); + + expect(wrapper.emitted()['click'][0]).toEqual([{}]); + expect(wrapper.emitted()['show'][0]).toEqual([]); + expect(wrapper.find('.p-speeddial').classes()).toContain('p-speeddial-opened'); + expect(wrapper.findAll('li.p-speeddial-item')[0].attributes().style).toBe('transition-delay: 0ms;'); + }); + + it('should hide the list', async () => { + await wrapper.setProps({ visible: true }); + + await wrapper.vm.onClick({}); + + expect(wrapper.find('.p-speeddial').classes()).not.toContain('p-speeddial-opened'); + expect(wrapper.findAll('li.p-speeddial-item')[wrapper.findAll('li.p-speeddial-item').length - 1].attributes().style).toBe('transition-delay: 0ms;'); + }); + + it('should have radius and type', async () => { + await wrapper.setProps({ radius: 80, direction: 'left', type: 'semi-circle' }); + + expect(wrapper.find('.p-speeddial').classes()).toContain('p-speeddial-semi-circle'); + expect(wrapper.find('.p-speeddial').classes()).toContain('p-speeddial-direction-left'); + }); + + it('should transition delay', async () => { + await wrapper.setProps({ transitionDelay: 80 }); + + expect(wrapper.findAll('li.p-speeddial-item')[wrapper.findAll('li.p-speeddial-item').length - 2].attributes().style).toBe('transition-delay: 80ms;'); + }); + + it('should have show and hide icons', async () => { + await wrapper.setProps({ showIcon: 'pi pi-bars', hideIcon: 'pi pi-times' }); + + const button = wrapper.find('.p-speeddial-button'); + + expect(button.find('span').classes()).toContain('pi-bars'); + + await wrapper.vm.onClick({}); + + expect(button.find('span').classes()).toContain('pi-times'); + }); + + it('should have mask', async () => { + await wrapper.setProps({ mask: true }); + + expect(wrapper.find('.p-speeddial-mask').exists()).toBe(true); + }); +}); \ No newline at end of file diff --git a/components/speeddial/SpeedDial.vue b/components/speeddial/SpeedDial.vue new file mode 100644 index 000000000..8c7f75e09 --- /dev/null +++ b/components/speeddial/SpeedDial.vue @@ -0,0 +1,382 @@ + + + + + diff --git a/components/speeddial/package.json b/components/speeddial/package.json new file mode 100644 index 000000000..22fac6211 --- /dev/null +++ b/components/speeddial/package.json @@ -0,0 +1,9 @@ +{ + "main": "./speeddial.cjs.js", + "module": "./speeddial.esm.js", + "unpkg": "./speeddial.min.js", + "types": "./SpeedDial.d.ts", + "browser": { + "./sfc": "./SpeedDial.vue" + } + } \ No newline at end of file diff --git a/components/splitbutton/SplitButton.d.ts b/components/splitbutton/SplitButton.d.ts new file mode 100755 index 000000000..467a47e8e --- /dev/null +++ b/components/splitbutton/SplitButton.d.ts @@ -0,0 +1,82 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; +import { MenuItem } from '../menuitem'; + +type SplitButtonAppendToType = 'body' | 'self' | string | undefined | HTMLElement; + +export interface SplitButtonProps { + /** + * Text of the button. + */ + label?: string | undefined; + /** + * Name of the icon. + */ + icon?: string | undefined; + /** + * MenuModel instance to define the overlay items. + */ + model?: MenuItem[] | undefined; + /** + * Whether to automatically manage layering. + * Default value is true. + */ + autoZIndex?: boolean | undefined; + /** + * Base zIndex value to use in layering. + * Default value is 0. + */ + baseZIndex?: number | undefined; + /** + * A valid query selector or an HTMLElement to specify where the overlay gets attached. + * @see SplitButtonAppendToType + * Default value is true. + */ + appendTo?: SplitButtonAppendToType; + /** + * Style class of the component. + */ + class?: any; + /** + * Inline style of the component. + */ + style?: any; +} + +export interface SplitButtonSlots { + /** + * Button part of the content can easily be customized with the default slot instead of using the built-in modes. + */ + default: () => VNode[]; +} + +export declare type SplitButtonEmits = { + /** + * Callback to invoke when main button is clicked. + * @param {Event} event - Browser event. + */ + 'click': (event: Event) => void; +} + +declare class SplitButton extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + SplitButton: GlobalComponentConstructor + } +} + +/** + * + * SplitButton groups a set of commands in an overlay with a default command. + * + * Helper API: + * + * - [MenuItem](https://www.primefaces.org/primevue/showcase/#/menumodel) + * + * Demos: + * + * - [SplitButton](https://www.primefaces.org/primevue/showcase/#/splitbutton) + * + */ +export default SplitButton; diff --git a/components/splitbutton/SplitButton.spec.js b/components/splitbutton/SplitButton.spec.js new file mode 100644 index 000000000..cbbe32b09 --- /dev/null +++ b/components/splitbutton/SplitButton.spec.js @@ -0,0 +1,59 @@ +import { mount } from '@vue/test-utils'; +import PrimeVue from '@/components/config/PrimeVue'; +import SplitButton from './SplitButton.vue'; + +describe('SplitButton.vue', () => { + let wrapper; + + beforeEach(async () => { + wrapper = mount(SplitButton, { + global: { + plugins: [PrimeVue], + stubs: { + teleport: true, + 'router-link': true + } + }, + props: { + label: 'Save', + model: [ + { + label: 'Update', + icon: 'pi pi-refresh' + }, + { + label: 'Delete', + icon: 'pi pi-times' + }, + { + label: 'Vue Website', + icon: 'pi pi-external-link', + command: () => { + window.location.href = 'https://vuejs.org/' + } + }, + { label: 'Upload', + icon: 'pi pi-upload', + to: '/fileupload' + } + ] + } + }); + + await wrapper.vm.onDropdownButtonClick(); + }); + + it('should exist', () => { + expect(wrapper.find('.p-splitbutton.p-component').exists()).toBe(true); + expect(wrapper.find('.p-tieredmenu.p-component').exists()).toBe(true); + expect(wrapper.findAll('li.p-menuitem').length).toBe(4); + expect(wrapper.find('.p-splitbutton-defaultbutton').exists()).toBe(true); + expect(wrapper.find('.p-button-label').text()).toBe('Save'); + }); + + it('should hide when default button is clicked', async () => { + await wrapper.vm.onDefaultButtonClick(); + + expect(wrapper.find('.p-tieredmenu.p-component').exists()).toBe(false); + }); +}); \ No newline at end of file diff --git a/components/splitbutton/SplitButton.vue b/components/splitbutton/SplitButton.vue new file mode 100755 index 000000000..91eaee0a0 --- /dev/null +++ b/components/splitbutton/SplitButton.vue @@ -0,0 +1,104 @@ + + + + + diff --git a/components/splitbutton/package.json b/components/splitbutton/package.json new file mode 100644 index 000000000..54193e4aa --- /dev/null +++ b/components/splitbutton/package.json @@ -0,0 +1,9 @@ +{ + "main": "./splitbutton.cjs.js", + "module": "./splitbutton.esm.js", + "unpkg": "./splitbutton.min.js", + "types": "./SplitButton.d.ts", + "browser": { + "./sfc": "./SplitButton.vue" + } +} \ No newline at end of file diff --git a/components/splitter/Splitter.d.ts b/components/splitter/Splitter.d.ts new file mode 100644 index 000000000..4e104052e --- /dev/null +++ b/components/splitter/Splitter.d.ts @@ -0,0 +1,79 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +type SplitterLayoutType = 'horizontal' | 'vertical' | undefined; + +type SplitterStateStorageType = 'local' | 'session' | undefined; + +export interface SplitterResizeEndEvent { + /** + * Browser event + */ + originalEvent: Event; + /** + * Sizes of the panels + */ + sizes: number[]; +} + +export interface SplitterProps { + /** + * Orientation of the panels. + * @see SplitterLayoutType + * Default value is 'horizontal'. + */ + layout?: SplitterLayoutType; + /** + * Size of the divider in pixels. + * Default value is 4. + */ + gutterSize?: number | undefined; + /** + * Storage identifier of a stateful Splitter. + */ + stateKey?: string | undefined; + /** + * Defines where a stateful splitter keeps its state, valid values are 'session' for sessionStorage and 'local' for localStorage. + * @see SplitterStateStorageType + * Default value is 'session'. + */ + stateStorage?: SplitterStateStorageType; +} + +export interface SplitterSlots { + /** + * Default slot to detect SplitterPanel components. + */ + default: () => VNode[]; +} + +export declare type SplitterEmits = { + /** + * Callback to invoke when resize ends. + * @param {SplitterResizeEndEvent} event - Custom resize end event. + */ + 'resizeend': (event: SplitterResizeEndEvent) => void; +} + +declare class Splitter extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Splitter: GlobalComponentConstructor + } +} + +/** + * + * Splitter is utilized to separate and resize panels. + * + * Helper Components: + * + * - SplitterPanel + * + * Demos: + * + * - [Splitter](https://www.primefaces.org/primevue/showcase/#/splitter) + * + */ +export default Splitter; diff --git a/components/splitter/Splitter.spec.js b/components/splitter/Splitter.spec.js new file mode 100644 index 000000000..d9e2788f8 --- /dev/null +++ b/components/splitter/Splitter.spec.js @@ -0,0 +1,42 @@ +import { mount } from '@vue/test-utils'; +import Splitter from './Splitter.vue'; +import SplitterPanel from '@/components/splitterpanel/SplitterPanel.vue'; + +describe('Splitter.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(Splitter, { + global: { + components: { + SplitterPanel + } + }, + slots: { + default: ` + + Panel 1 + + + Panel 2 + + ` + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-splitter.p-component').exists()).toBe(true); + expect(wrapper.find('.p-splitter').classes()).toContain('p-splitter-horizontal'); + expect(wrapper.findAll('.p-splitter-panel').length).toBe(2); + expect(wrapper.find('.p-splitter-gutter-handle').exists()).toBe(true); + }); + + it('should mousedown', async () => { + const gutter = wrapper.find('.p-splitter-gutter-handle').element; + const siblings = wrapper.findAll('.p-splitter-panel'); + await wrapper.vm.onGutterMouseDown({ currentTarget: {gutter, previousElementSibling: siblings[0].element, nextElementSibling: siblings[1].element }, pageX: 123 }, 0); + + expect(wrapper.find('.p-splitter').classes()).toContain('p-splitter-resizing'); + }); +}); \ No newline at end of file diff --git a/components/splitter/Splitter.vue b/components/splitter/Splitter.vue new file mode 100644 index 000000000..fdd08ce2a --- /dev/null +++ b/components/splitter/Splitter.vue @@ -0,0 +1,344 @@ + + + + + diff --git a/components/splitter/package.json b/components/splitter/package.json new file mode 100644 index 000000000..88560cbf2 --- /dev/null +++ b/components/splitter/package.json @@ -0,0 +1,9 @@ +{ + "main": "./splitter.cjs.js", + "module": "./splitter.esm.js", + "unpkg": "./splitter.min.js", + "types": "./Splitter.d.ts", + "browser": { + "./sfc": "./Splitter.vue" + } +} \ No newline at end of file diff --git a/components/splitterpanel/SplitterPanel.d.ts b/components/splitterpanel/SplitterPanel.d.ts new file mode 100644 index 000000000..86f913053 --- /dev/null +++ b/components/splitterpanel/SplitterPanel.d.ts @@ -0,0 +1,42 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; + +export interface SplitterPanelProps { + /** + * Size of the element relative to 100%. + */ + size?: number | undefined; + /** + * Minimum size of the element relative to 100%. + */ + minSize?: number | undefined; +} + +export interface SplitterPanelSlots { + /** + * Custom content template. + */ + default: () => VNode[]; +} + +export declare type SplitterPanelEmits = { +} + +declare class SplitterPanel extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + SplitterPanel: GlobalComponentConstructor + } +} + +/** + * + * SplitterPanel is a helper component for Splitter component. + * + * Demos: + * + * - [SplitterPanel](https://www.primefaces.org/primevue/showcase/#/splitter) + * + */ +export default SplitterPanel; diff --git a/components/splitterpanel/SplitterPanel.spec.js b/components/splitterpanel/SplitterPanel.spec.js new file mode 100644 index 000000000..d1f18461c --- /dev/null +++ b/components/splitterpanel/SplitterPanel.spec.js @@ -0,0 +1,23 @@ +import { mount } from '@vue/test-utils'; +import SplitterPanel from './SplitterPanel.vue'; + +describe('SplitterPanel.vue', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(SplitterPanel, { + attrs: { + class: 'flex align-items-center justify-content-center' + }, + slots: { + default: 'Panel 1' + } + }); + }); + + it('should exist', () => { + expect(wrapper.find('.p-splitter-panel').exists()).toBe(true); + expect(wrapper.attributes().class).toBe('p-splitter-panel flex align-items-center justify-content-center'); + expect(wrapper.find('.p-splitter-panel').text()).toBe('Panel 1'); + }); +}); \ No newline at end of file diff --git a/components/splitterpanel/SplitterPanel.vue b/components/splitterpanel/SplitterPanel.vue new file mode 100644 index 000000000..3bd15e4b4 --- /dev/null +++ b/components/splitterpanel/SplitterPanel.vue @@ -0,0 +1,31 @@ + + + diff --git a/components/splitterpanel/package.json b/components/splitterpanel/package.json new file mode 100644 index 000000000..25db20614 --- /dev/null +++ b/components/splitterpanel/package.json @@ -0,0 +1,9 @@ +{ + "main": "./splitterpanel.cjs.js", + "module": "./splitterpanel.esm.js", + "unpkg": "./splitterpanel.min.js", + "types": "./SplitterPanel.d.ts", + "browser": { + "./sfc": "./SplitterPanel.vue" + } +} \ No newline at end of file diff --git a/components/steps/Steps.d.ts b/components/steps/Steps.d.ts new file mode 100755 index 000000000..c726f3239 --- /dev/null +++ b/components/steps/Steps.d.ts @@ -0,0 +1,59 @@ +import { VNode } from 'vue'; +import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; +import { MenuItem } from '../menuitem'; + +export interface StepsProps { + /** + * Unique identifier of the element. + */ + id?: string | undefined; + /** + * An array of menuitems. + */ + model?: MenuItem[] | undefined; + /** + * Whether the items are clickable or not. + * Default value is true. + */ + readonly?: boolean | undefined; + /** + * Whether to apply 'router-link-active-exact' class if route exactly matches the item path. + * Default value is true. + */ + exact?: boolean | undefined; +} + +export interface StepsSlots { + /** + * Custom item template. + * @param {Object} scope - item slot's params. + */ + item: (scope: { + /** + * Menuitem instance + */ + item: MenuItem; + }) => VNode[]; +} + +export declare type StepsEmits = { +} + +declare class Steps extends ClassComponent { } + +declare module '@vue/runtime-core' { + interface GlobalComponents { + Steps: GlobalComponentConstructor + } +} + +/** + * + * Steps components is an indicator for the steps in a wizard workflow. Example below uses nested routes with Steps. + * + * Demos: + * + * - [Steps](https://www.primefaces.org/primevue/showcase/#/steps) + * + */ +export default Steps; diff --git a/components/steps/Steps.vue b/components/steps/Steps.vue new file mode 100755 index 000000000..59548a98f --- /dev/null +++ b/components/steps/Steps.vue @@ -0,0 +1,150 @@ + + + + + diff --git a/components/steps/package.json b/components/steps/package.json new file mode 100644 index 000000000..908d919bd --- /dev/null +++ b/components/steps/package.json @@ -0,0 +1,9 @@ +{ + "main": "./steps.cjs.js", + "module": "./steps.esm.js", + "unpkg": "./steps.min.js", + "types": "./Steps.d.ts", + "browser": { + "./sfc": "./Steps.vue" + } +} \ No newline at end of file diff --git a/components/styleclass/StyleClass.d.ts b/components/styleclass/StyleClass.d.ts new file mode 100644 index 000000000..e21fdfd5c --- /dev/null +++ b/components/styleclass/StyleClass.d.ts @@ -0,0 +1,5 @@ +import { ObjectDirective } from 'vue'; + +declare const StyleClass: ObjectDirective; + +export default StyleClass; diff --git a/components/styleclass/StyleClass.js b/components/styleclass/StyleClass.js new file mode 100644 index 000000000..871232c54 --- /dev/null +++ b/components/styleclass/StyleClass.js @@ -0,0 +1,177 @@ +import {DomHandler} from 'primevue/utils'; + +function bind(el, binding) { + el.$_pstyleclass_clicklistener = () => { + const target = resolveTarget(el, binding); + + if (binding.value.toggleClass) { + if (DomHandler.hasClass(target, binding.value.toggleClass)) + DomHandler.removeClass(target, binding.value.toggleClass); + else + DomHandler.addClass(target, binding.value.toggleClass); + } + else { + if (target.offsetParent === null) + enter(target, el, binding); + else + leave(target, binding); + } + }; + el.addEventListener('click', el.$_pstyleclass_clicklistener); +} + +function unbind(el) { + if (el.$_pstyleclass_clicklistener) { + el.addEventListener('click', el.$_pstyleclass_clicklistener); + el.$_pstyleclass_clicklistener = null; + } + + unbindDocumentListener(el); +} + +function enter(target, el, binding) { + if (binding.value.enterActiveClass) { + if (!target.$_pstyleclass_animating) { + target.$_pstyleclass_animating = true; + + if (binding.value.enterActiveClass === 'slidedown') { + target.style.height = '0px'; + DomHandler.removeClass(target, 'hidden'); + target.style.maxHeight = target.scrollHeight + 'px'; + DomHandler.addClass(target, 'hidden'); + target.style.height = ''; + } + + DomHandler.addClass(target, binding.value.enterActiveClass); + if (binding.value.enterClass) { + DomHandler.removeClass(target, binding.value.enterClass); + } + + target.$p_styleclass_enterlistener = () => { + DomHandler.removeClass(target, binding.value.enterActiveClass); + if (binding.value.enterToClass) { + DomHandler.addClass(target, binding.value.enterToClass); + } + target.removeEventListener('animationend', target.$p_styleclass_enterlistener); + + if (binding.value.enterActiveClass === 'slidedown') { + target.style.maxHeight = ''; + } + + target.$_pstyleclass_animating = false; + }; + + target.addEventListener('animationend', target.$p_styleclass_enterlistener); + } + } + else { + if (binding.value.enterClass) { + DomHandler.removeClass(target, binding.value.enterClass); + } + + if (binding.value.enterToClass) { + DomHandler.addClass(target, binding.value.enterToClass); + } + } + + if (binding.value.hideOnOutsideClick) { + bindDocumentListener(target, el, binding); + } +} + +function leave(target, binding) { + if (binding.value.leaveActiveClass) { + if (!target.$_pstyleclass_animating) { + target.$_pstyleclass_animating = true; + DomHandler.addClass(target, binding.value.leaveActiveClass); + if (binding.value.leaveClass) { + DomHandler.removeClass(target, binding.value.leaveClass); + } + + target.$p_styleclass_leavelistener = () => { + DomHandler.removeClass(target, binding.value.leaveActiveClass); + if (binding.value.leaveToClass) { + DomHandler.addClass(target, binding.value.leaveToClass); + } + target.removeEventListener('animationend', target.$p_styleclass_leavelistener); + target.$_pstyleclass_animating = false; + }; + + target.addEventListener('animationend', target.$p_styleclass_leavelistener); + } + } + else { + if (binding.value.leaveClass) { + DomHandler.removeClass(target, binding.value.leaveClass); + } + + if (binding.value.leaveToClass) { + DomHandler.addClass(target, binding.value.leaveToClass); + } + } + + if (binding.value.hideOnOutsideClick) { + unbindDocumentListener(target); + } +} + +function resolveTarget(el, binding) { + switch (binding.value.selector) { + case '@next': + return el.nextElementSibling; + + case '@prev': + return el.previousElementSibling; + + case '@parent': + return el.parentElement; + + case '@grandparent': + return el.parentElement.parentElement; + + default: + return document.querySelector(binding.value.selector); + } +} + +function bindDocumentListener(target, el, binding) { + if (!target.$p_styleclass_documentlistener) { + target.$p_styleclass_documentlistener = (event) => { + if (!isVisible(target) || getComputedStyle(target).getPropertyValue('position') === 'static') { + unbindDocumentListener(target); + } + + else if (isOutsideClick(event, target, el)) { + leave(target, binding); + } + } + + target.ownerDocument.addEventListener('click', target.$p_styleclass_documentlistener); + } +} + +function unbindDocumentListener(target) { + if (target.$p_styleclass_documentlistener) { + target.ownerDocument.removeEventListener('click', target.$p_styleclass_documentlistener); + target.$p_styleclass_documentlistener = null; + } +} + +function isVisible(target) { + return target.offsetParent !== null; +} + +function isOutsideClick(event, target, el) { + return !el.isSameNode(event.target) && !el.contains(event.target) && !target.contains(event.target); +} + +const StyleClass = { + mounted(el, binding) { + bind(el, binding); + }, + unmounted(el) { + unbind(el); + } +}; + +export default StyleClass; \ No newline at end of file diff --git a/components/styleclass/StyleClass.spec.js b/components/styleclass/StyleClass.spec.js new file mode 100644 index 000000000..821c9abe8 --- /dev/null +++ b/components/styleclass/StyleClass.spec.js @@ -0,0 +1,36 @@ +import { config, mount } from '@vue/test-utils'; +import StyleClass from './StyleClass'; +import Button from '@/components/button/Button.vue'; +import InputText from '@/components/inputtext/InputText.vue'; + +config.global.directives = { + 'styleclass': StyleClass +} + +describe('StyleClass', () => { + it('should work with next selector', async () => { + const wrapper = mount({ + template: ` +