diff --git a/src/App.vue b/src/App.vue index a0d7a19..46362c7 100644 --- a/src/App.vue +++ b/src/App.vue @@ -2,4 +2,4 @@
Vue-Tailwind
- + \ No newline at end of file diff --git a/src/assets/icons/FirstArrow.vue b/src/assets/icons/FirstArrow.vue new file mode 100644 index 0000000..9f22cf9 --- /dev/null +++ b/src/assets/icons/FirstArrow.vue @@ -0,0 +1,12 @@ + diff --git a/src/assets/icons/IconCheck.vue b/src/assets/icons/IconCheck.vue new file mode 100644 index 0000000..b2d0e2e --- /dev/null +++ b/src/assets/icons/IconCheck.vue @@ -0,0 +1,11 @@ + diff --git a/src/assets/icons/IconDash.vue b/src/assets/icons/IconDash.vue new file mode 100644 index 0000000..90c5865 --- /dev/null +++ b/src/assets/icons/IconDash.vue @@ -0,0 +1,5 @@ + diff --git a/src/assets/icons/IconFilter.vue b/src/assets/icons/IconFilter.vue new file mode 100644 index 0000000..15c61a3 --- /dev/null +++ b/src/assets/icons/IconFilter.vue @@ -0,0 +1,5 @@ + diff --git a/src/assets/icons/IonLoader.vue b/src/assets/icons/IonLoader.vue new file mode 100644 index 0000000..f29bb1a --- /dev/null +++ b/src/assets/icons/IonLoader.vue @@ -0,0 +1,37 @@ + diff --git a/src/assets/icons/LastArrow.vue b/src/assets/icons/LastArrow.vue new file mode 100644 index 0000000..79df098 --- /dev/null +++ b/src/assets/icons/LastArrow.vue @@ -0,0 +1,9 @@ + diff --git a/src/assets/icons/NextArrow.vue b/src/assets/icons/NextArrow.vue new file mode 100644 index 0000000..6177f13 --- /dev/null +++ b/src/assets/icons/NextArrow.vue @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/src/assets/icons/PrevArrow.vue b/src/assets/icons/PrevArrow.vue new file mode 100644 index 0000000..d57a59b --- /dev/null +++ b/src/assets/icons/PrevArrow.vue @@ -0,0 +1,9 @@ + diff --git a/src/components/__tests__/DTable.spec.ts b/src/components/__tests__/DTable.spec.ts new file mode 100644 index 0000000..db85d64 --- /dev/null +++ b/src/components/__tests__/DTable.spec.ts @@ -0,0 +1,363 @@ +import { describe, it, expect } from 'vitest' +import { mount } from '@vue/test-utils' +import ColumnHeader from '../data-table/ColumnHeader.vue' +import ColumnFilter from '../data-table/ColumnFilter.vue' +import FooterPagination from '../data-table/FooterPagination.vue' +import DTable from '../data-table/DTable.vue' + +describe('ColumnFilter.vue', () => { + // Mock data for testing + const propsData = { + column: { + type: 'string', + condition: 'contain', + value: '' + }, + columnFilterLang: { + no_filter: 'No filter', + contain: 'Contain', + not_contain: 'Not contain', + equal: 'Equal', + not_equal: 'Not equal', + start_with: 'Starts with', + end_with: 'Ends with', + greater_than: 'Greater than', + greater_than_equal: 'Greater than or equal', + less_than: 'Less than', + less_than_equal: 'Less than or equal', + is_null: 'Is null', + is_not_null: 'Not null' + } + } + + const wrapper: any = mount(ColumnFilter, { props: propsData }) + + // Test for rendering with correct props + it('renders with correct props', async () => { + // Check if the component renders with the correct props + expect(wrapper.props()).toEqual(propsData) + }) + + // Test for selecting a filter condition + it('emits filterChange event when a filter condition is selected', async () => { + // You can simulate the user selecting a filter condition, e.g., 'contain' + await wrapper.find('button.active').trigger('click') + + // Check if the filterChange event is emitted with the correct data + expect(wrapper.emitted().filterChange).toBeTruthy() + expect(wrapper.emitted().filterChange[0][0]).toEqual({ + type: 'string', + condition: 'contain', + value: '' + }) + }) +}) + +describe('ColumnHeader.vue', () => { + // Mock data for testing + const propsData = { + all: { + hasCheckbox: true, + stickyHeader: true, + stickyFirstColumn: false, + columns: [ + { field: 'id', title: 'ID', hide: false, sort: true, width: '100px' }, + { field: 'name', title: 'Name', hide: false, sort: true, width: '150px' } + // Add more sample columns as needed + ] + // Add other props as needed + }, + currentSortColumn: 'id', + currentSortDirection: 'asc', + isOpenFilter: null, + isFooter: false, + checkAll: 0, + columnFilterLang: 'en' // Replace with your language preference + } + + const wrapper = mount(ColumnHeader, { props: propsData }) + + // Test for rendering with correct props + it('renders with correct props', async () => { + // Check if the component renders with the correct props + expect(wrapper.props()).toEqual(propsData) + }) + + // Test for selectAll event + it('emits selectAll event when checkbox is clicked', async () => { + await wrapper.find('#select').trigger('click') + expect(wrapper.emitted().selectAll).toBeTruthy() + }) +}) + +describe('FooterPagination.vue', () => { + it('renders correctly', () => { + const props = { + totalRows: 100, + offset: 1, + limit: 10, + page: 1, + showPageSize: true, + pageSizeOptions: [10, 20, 30] + } + + mount(FooterPagination, { props }) + }) + + it('displays the correct initial page information', () => { + const props = { + totalRows: 100, + offset: 1, + limit: 10, + page: 1, + showPageSize: true, + pageSizeOptions: [10, 20, 30] + } + + const wrapper = mount(FooterPagination, { props }) + + expect(wrapper.find('.bh-pagination-info').text()).toContain('Showing 1 to 10 of 100 entries') + }) + + it('emits "changePage" event when a page button is clicked', async () => { + const props = { + totalRows: 100, + offset: 1, + limit: 10, + page: 1, + showPageSize: true, + pageSizeOptions: [10, 20, 30] + } + + const wrapper: any = mount(FooterPagination, { props }) + + await wrapper.find('.bh-page-item:nth-child(1)').trigger('click') + + expect(wrapper.emitted().changePage).toBeTruthy() + expect(wrapper.emitted().changePage[0][0]).toBe(1) + }) + + it('emits "changePage" event when "Next" button is clicked', async () => { + const props = { + totalRows: 100, + offset: 1, + limit: 10, + page: 1, + showPageSize: true, + pageSizeOptions: [10, 20, 30] + } + + const wrapper: any = mount(FooterPagination, { props }) + + await wrapper.find('.bh-page-item.next-page').trigger('click') + + expect(wrapper.emitted().changePage).toBeTruthy() + expect(wrapper.emitted().changePage[0][0]).toBe(2) + }) + + it('emits "changePage" event when "Previous" button is clicked', async () => { + const props = { + totalRows: 100, + offset: 1, + limit: 10, + page: 2, + showPageSize: true, + pageSizeOptions: [10, 20, 30] + } + + const wrapper: any = mount(FooterPagination, { props }) + + await wrapper.find('.bh-page-item.previous-page').trigger('click') + + expect(wrapper.emitted().changePage).toBeTruthy() + expect(wrapper.emitted().changePage[0][0]).toBe(1) + }) + + it('emits "changePage" event when "First" button is clicked', async () => { + const props = { + totalRows: 100, + offset: 1, + limit: 10, + page: 2, + showPageSize: true, + pageSizeOptions: [10, 20, 30] + } + + const wrapper: any = mount(FooterPagination, { props }) + + await wrapper.find('.bh-page-item.first-page').trigger('click') + + expect(wrapper.emitted().changePage).toBeTruthy() + expect(wrapper.emitted().changePage[0][0]).toBe(1) + }) + + it('emits "changePage" event when "Last" button is clicked', async () => { + const props = { + totalRows: 100, + offset: 1, + limit: 10, + page: 1, + showPageSize: true, + pageSizeOptions: [10, 20, 30] + } + + const wrapper: any = mount(FooterPagination, { props }) + + await wrapper.find('.bh-page-item.last-page').trigger('click') + + expect(wrapper.emitted().changePage).toBeTruthy() + expect(wrapper.emitted().changePage[0][0]).toBe(10) + }) +}) + +describe('DTable.vue', () => { + it('renders with correct props and emits change event', async () => { + // Mock data for testing + const propsData = { + loading: false, + isServerMode: false, + totalRows: 100, + rows: [ + { id: 1, name: 'John Doe', age: 25 } + // Add more sample rows as needed + ], + columns: [ + { field: 'id', label: 'ID' }, + { field: 'name', label: 'Name' }, + { field: 'age', label: 'Age' } + // Add more sample columns as needed + ], + hasCheckbox: true, + search: 'Sample Search', + columnChooser: false, + page: 1, + pageSize: 10, + pageSizeOptions: [10, 20, 30, 50, 100], + showPageSize: true, + rowClass: null, + cellClass: null, + sortable: true, + sortColumn: 'id', + sortDirection: 'asc', + columnFilter: true, + columnFilterLang: null, + pagination: true, + showNumbers: true, + showNumbersCount: 5, + showFirstPage: true, + showLastPage: true, + firstArrow: '', + lastArrow: '', + nextArrow: '', + previousArrow: '', + paginationInfo: 'Showing {0} to {1} of {2} entries', + noDataContent: 'No data available', + stickyHeader: false, + height: '500px', + stickyFirstColumn: false, + cloneHeaderInFooter: false, + header: true, + selectRowOnClick: false + } + + const wrapper = mount(DTable, { props: propsData }) + // Check if the component renders with the correct props + expect(wrapper.props()).toEqual(propsData) + }) + + // Test for the "change" event + it('emits the "change" event when triggered', async () => { + const wrapper = mount(DTable) + await wrapper.vm.$emit('change') + expect(wrapper.emitted().change).toBeTruthy() + }) + + // Test for the "sortChange" event + it('emits the "sortChange" event when triggered', async () => { + const wrapper = mount(DTable) + await wrapper.vm.$emit('sortChange') + expect(wrapper.emitted().sortChange).toBeTruthy() + }) + + // Test for the "searchChange" event + it('emits the "searchChange" event when triggered', async () => { + const wrapper = mount(DTable) + await wrapper.vm.$emit('searchChange') + expect(wrapper.emitted().searchChange).toBeTruthy() + }) + + // Test for the "pageChange" event + it('emits the "pageChange" event when triggered', async () => { + const wrapper = mount(DTable) + await wrapper.vm.$emit('pageChange') + expect(wrapper.emitted().pageChange).toBeTruthy() + }) + + // Test for the "pageSizeChange" event + it('emits the "pageSizeChange" event when triggered', async () => { + const wrapper = mount(DTable) + await wrapper.vm.$emit('pageSizeChange') + expect(wrapper.emitted().pageSizeChange).toBeTruthy() + }) + + // Test for the "rowSelect" event + it('emits the "rowSelect" event when triggered', async () => { + const wrapper = mount(DTable) + await wrapper.vm.$emit('rowSelect') + expect(wrapper.emitted().rowSelect).toBeTruthy() + }) + + // Test for the "filterChange" event + it('emits the "filterChange" event when triggered', async () => { + const wrapper = mount(DTable) + await wrapper.vm.$emit('filterChange') + expect(wrapper.emitted().filterChange).toBeTruthy() + }) + + // Test for the "rowClick" event + it('emits the "rowClick" event when triggered', async () => { + const wrapper = mount(DTable) + await wrapper.vm.$emit('rowClick') + expect(wrapper.emitted().rowClick).toBeTruthy() + }) + + // Test for the "rowDBClick" event + it('emits the "rowDBClick" event when triggered', async () => { + const wrapper = mount(DTable) + await wrapper.vm.$emit('rowDBClick') + expect(wrapper.emitted().rowDBClick).toBeTruthy() + }) + + // Test for the "changePageSize" event + it('emits the "changePageSize" event when triggered', async () => { + const wrapper = mount(DTable) + await wrapper.vm.$emit('changePageSize') + expect(wrapper.emitted().changePageSize).toBeTruthy() + }) + + // Test for the "changePage" event + it('emits the "changePage" event when triggered', async () => { + const wrapper = mount(DTable) + await wrapper.vm.$emit('changePage') + expect(wrapper.emitted().changePage).toBeTruthy() + }) + + it('renders table with correct number of rows', async () => { + const wrapper = mount(DTable, { + props: { + rows: [ + { id: 1, name: 'John Doe', age: 30 }, + { id: 2, name: 'Jane Doe', age: 25 } + ], + columns: [ + { field: 'id', type: 'number' }, + { field: 'name', type: 'string' }, + { field: 'age', type: 'number' } + ] + } + }) + + await wrapper.vm.$nextTick() + expect(wrapper.findAll('tbody tr').length).toBe(2) + }) +}) diff --git a/src/components/__tests__/__snapshots__/DTable.spec.ts.snap b/src/components/__tests__/__snapshots__/DTable.spec.ts.snap new file mode 100644 index 0000000..5067513 --- /dev/null +++ b/src/components/__tests__/__snapshots__/DTable.spec.ts.snap @@ -0,0 +1,23 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`FooterPagination.vue > renders correctly 1`] = ` +"
+
+
Showing 1 to 10 of 100 entries
+
+
+
" +`; diff --git a/src/components/data-table/ColumnFilter.vue b/src/components/data-table/ColumnFilter.vue new file mode 100644 index 0000000..053d1f5 --- /dev/null +++ b/src/components/data-table/ColumnFilter.vue @@ -0,0 +1,186 @@ + + + + diff --git a/src/components/data-table/ColumnHeader.vue b/src/components/data-table/ColumnHeader.vue new file mode 100644 index 0000000..69cd3f8 --- /dev/null +++ b/src/components/data-table/ColumnHeader.vue @@ -0,0 +1,162 @@ + + diff --git a/src/components/data-table/DTable.vue b/src/components/data-table/DTable.vue new file mode 100644 index 0000000..86e0588 --- /dev/null +++ b/src/components/data-table/DTable.vue @@ -0,0 +1,949 @@ + + + + + diff --git a/src/components/data-table/FooterPagination.vue b/src/components/data-table/FooterPagination.vue new file mode 100644 index 0000000..24ad762 --- /dev/null +++ b/src/components/data-table/FooterPagination.vue @@ -0,0 +1,215 @@ + + + diff --git a/src/index.ts b/src/index.ts index ee2ff8d..4667e0d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ -import DButton from "./components/DButton.vue" -import DInput from "./components/DInput.vue" +import DButton from './components/DButton.vue' +import DInput from './components/DInput.vue' +import DTable from './components/data-table/DTable.vue' -export default {DButton, DInput} \ No newline at end of file +export default { DButton, DInput, DTable }