Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/demo/src/app-routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import Options37 from './options/options37.js';
import Options38 from './options/options38.js';
import Options39 from './options/options39.js';
import Options40 from './options/options40.js';
import Options41 from './options/options41.js';

export const navbarRouting = [
{ name: 'getting-started', view: '/src/getting-started.html', viewModel: GettingStarted, title: 'Getting Started' },
Expand Down Expand Up @@ -139,7 +140,8 @@ export const exampleRouting = [
{ name: 'options37', view: '/src/options/options37.html', viewModel: Options37, title: 'Navigation Highlight' },
{ name: 'options38', view: '/src/options/options38.html', viewModel: Options38, title: 'Dark Mode' },
{ name: 'options39', view: '/src/options/options39.html', viewModel: Options39, title: 'Label Id (aria-labelledby)' },
{ name: 'options40', view: '/src/options/options40.html', viewModel: Options40, title: 'Filter Data' },
{ name: 'options40', view: '/src/options/options40.html', viewModel: Options40, title: 'Pre-Filter Data' },
{ name: 'options41', view: '/src/options/options41.html', viewModel: Options41, title: 'Pre-Sort Data' },
],
},
{
Expand Down
4 changes: 2 additions & 2 deletions packages/demo/src/options/options40.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div class="row mb-2">
<div class="col-md-12 title-desc">
<h2 class="bd-title">
Filter Data
Pre-Filter Data
<span class="float-end links">
Code <span class="fa fa-link"></span>
<span class="small">
Expand All @@ -17,7 +17,7 @@ <h2 class="bd-title">
</span>
</span>
</h2>
<div class="demo-subtitle">Use <code>preFilter(predicate)</code> to pre-filter the data collection before rendering the select dropdown in the UI.</div>
<div class="demo-subtitle">Use <code>preFilter: predicate</code> to pre-filter the data collection before rendering the select dropdown in the UI.</div>
</div>
</div>

Expand Down
87 changes: 87 additions & 0 deletions packages/demo/src/options/options41.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<div class="row mb-2">
<div class="col-md-12 title-desc">
<h2 class="bd-title">
Pre-Sort Data
<span class="float-end links">
Code <span class="fa fa-link"></span>
<span class="small">
<a
target="_blank"
href="https://github.com/ghiscoding/multiple-select-vanilla/blob/main/packages/demo/src/options/options41.html"
>html</a
>
|
<a target="_blank" href="https://github.com/ghiscoding/multiple-select-vanilla/blob/main/packages/demo/src/options/options41.ts"
>ts</a
>
</span>
</span>
</h2>
<div class="demo-subtitle">Use <code>preSort: comparer</code> to pre-sort the data collection before rendering the select dropdown in the UI.</div>
</div>
</div>

<div>
<div class="mb-3 row">
<label class="col-sm-2">Basic Select</label>

<div class="col-sm-10">
<select multiple="multiple" class="full-width" data-test="select1">
<option value="1">January</option>
<option value="2">February</option>
<option value="3">March</option>
<option value="4">April</option>
<option value="5">May</option>
<option value="6">June</option>
<option value="7">July</option>
<option value="8">August</option>
<option value="9">September</option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</select>
</div>
</div>

<div class="mb-3 row">
<label class="col-sm-2"> Group Select </label>

<div class="col-sm-10">
<select multiple="multiple" class="full-width" data-test="select2">
<optgroup label="Group 1">
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</optgroup>
<optgroup label="Group 2">
<option value="4">Option 4</option>
<option value="5">Option 5</option>
<option value="6">Option 6</option>
</optgroup>
<optgroup label="Group 3">
<option value="7">Option 7</option>
<option value="8">Option 8</option>
<option value="9">Option 9</option>
</optgroup>
</select>
</div>
</div>

<div class="mb-3 row">
<label class="col-sm-2">Basic Select (from data)</label>

<div class="col-sm-10">
<select multiple="multiple" class="full-width" data-test="select3">
</select>
</div>
</div>

<div class="mb-3 row">
<label class="col-sm-2"> Group Select (from data)</label>

<div class="col-sm-10">
<select multiple="multiple" class="full-width" data-test="select4">
</select>
</div>
</div>
</div>
153 changes: 153 additions & 0 deletions packages/demo/src/options/options41.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { type MultipleSelectInstance, type OptGroupRowData, multipleSelect } from 'multiple-select-vanilla';

export default class Example {
ms1?: MultipleSelectInstance;
ms2?: MultipleSelectInstance;
ms3?: MultipleSelectInstance;
ms4?: MultipleSelectInstance;

mount() {
this.ms1 = multipleSelect('select[data-test=select1]', {
filter: true,
preSort: (item1, item2) => {
// sort by value in reverse order
const direction = -1; // reverse order
return (+item1.value! - +item2.value!) * direction;
},
}) as MultipleSelectInstance;

this.ms2 = multipleSelect('select[data-test=select2]', {
filter: true,
preSort: (item1, item2) => {
// sort by value in reverse order
const direction = -1; // reverse order
// @ts-ignore
if (direction === 1) {
return (item1 as OptGroupRowData).label < (item2 as OptGroupRowData).label ? -1 : 1;
}
return (item1 as OptGroupRowData).label < (item2 as OptGroupRowData).label ? 1 : -1;
},
}) as MultipleSelectInstance;

this.ms3 = multipleSelect('select[data-test=select3]', {
data: {
1: 'January',
2: 'February',
3: 'March',
4: 'April',
5: 'May',
6: 'June',
7: 'July',
8: 'August',
9: 'September',
10: 'October',
11: 'November',
12: 'December',
},
preSort: (item1, item2) => {
// sort by value in reverse order
const direction = -1; // reverse order
return (+item1.value! - +item2.value!) * direction;
},
}) as MultipleSelectInstance;

this.ms4 = multipleSelect('select[data-test=select4]', {
data: [
{
type: 'optgroup',
label: 'Q1',
children: [
{
text: 'January',
value: 1,
selected: true,
},
{
text: 'February',
value: 2,
},
{
text: 'March',
value: 3,
},
],
},
{
type: 'optgroup',
label: 'Q2',
children: [
{
text: 'April',
value: 4,
},
{
text: 'May',
value: 5,
},
{
text: 'June',
value: 6,
},
],
},
{
type: 'optgroup',
label: 'Q3',
children: [
{
text: 'July',
value: 7,
},
{
text: 'August',
value: 8,
},
{
text: 'September',
value: 9,
},
],
},
{
type: 'optgroup',
label: 'Q4',
children: [
{
text: 'October',
value: 10,
},
{
text: 'November',
value: 11,
},
{
text: 'December',
value: 12,
},
],
},
],
preSort: (item1, item2) => {
// sort by value in reverse order
const direction = -1; // reverse order
// @ts-ignore
if (direction === 1) {
return (item1 as OptGroupRowData).label < (item2 as OptGroupRowData).label ? -1 : 1;
}
return (item1 as OptGroupRowData).label < (item2 as OptGroupRowData).label ? 1 : -1;
},
}) as MultipleSelectInstance;
}

unmount() {
// destroy ms instance(s) to avoid DOM leaks
this.ms1?.destroy();
this.ms2?.destroy();
this.ms3?.destroy();
this.ms4?.destroy();
this.ms1 = undefined;
this.ms2 = undefined;
this.ms3 = undefined;
this.ms4 = undefined;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -316,11 +316,16 @@ export class MultipleSelectInstance {
this.fromHtml = true;
}

// user might filter its data prior to rendering the list
// user might pre-filter its data prior to rendering the list
if (this.data && this.options.preFilter) {
this.data = this.data.filter(this.options.preFilter);
}

// user might pre-sort its data prior to rendering the list
if (this.data && this.options.preSort) {
this.data = this.data.sort(this.options.preSort);
}

this.dataTotal = setDataKeys(this.data || []);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,18 @@ export interface MultipleSelectOption extends MultipleSelectLocale {
/** Defines the position of select dropdown, can only be bottom or top. By default this option is set to bottom. */
position: 'bottom' | 'top';

/** Custom Filter predicate function to pre-filter the data collection before rendering the select dropdown. Executed before `preSort`. */
/**
* Custom Filter predicate function callback to pre-filter the data collection before rendering the select dropdown.
* Note that this is executed before `preSort` (when defined).
*/
preFilter?: (dataItem: OptionRowData | OptGroupRowData) => boolean;

/**
* Custom Sort Comparer function callback to pre-sort the data collection before rendering the select dropdown.
* Note that this is executed after `preSort` (when defined).
*/
preSort?: (item1: OptionRowData | OptGroupRowData, item2: OptionRowData | OptGroupRowData) => number;

/** Defaults to False, should we render option labels as html? */
renderOptionLabelAsHtml?: boolean;

Expand Down
6 changes: 3 additions & 3 deletions playwright/e2e/options40.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { test, expect } from '@playwright/test';

test.describe('Options 40 - Filter Data', () => {
test('all select dropdown should have data filtered out', async ({ page }) => {
test.describe('Options 40 - Pre-Filter Data', () => {
test('all select dropdown should have data pre-filtered', async ({ page }) => {
await page.goto('#/options40');

// 1st select
Expand Down Expand Up @@ -35,7 +35,7 @@ test.describe('Options 40 - Filter Data', () => {
await expect(await page.getByRole('option', { name: 'March' })).toHaveCount(1);
await expect(await page.getByRole('option', { name: 'April' })).toHaveCount(0);
await expect(await page.getByRole('option', { name: 'May' })).toHaveCount(1);
const select3LiElms = await page.locator('div[data-test=select1] li[role="option"]');
const select3LiElms = await page.locator('div[data-test=select3] li[role="option"]');
await expect(select3LiElms).toHaveCount(10);
await page.locator('div[data-test=select3].ms-parent').click();

Expand Down
51 changes: 51 additions & 0 deletions playwright/e2e/options41.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { test, expect } from '@playwright/test';

test.describe('Options 41 - Pre-Sort Data', () => {
test('all select dropdown should have data pre-filtered', async ({ page }) => {
await page.goto('#/options41');

// 1st select
await page.locator('div[data-test=select1].ms-parent').click();
const select1LiElms = await page.locator('div[data-test=select1] li[role="option"]');
await expect(select1LiElms).toHaveCount(12);
const select1li1 = await page.locator('div[data-test=select1] .ms-drop li').nth(0);
await expect(await select1li1.locator('label')).toHaveText('December');
const select1li2 = await page.locator('div[data-test=select1] .ms-drop li').nth(1);
await expect(await select1li2.locator('label')).toHaveText('November');
const select1li3 = await page.locator('div[data-test=select1] .ms-drop li').nth(2);
await expect(await select1li3.locator('label')).toHaveText('October');
await page.locator('div[data-test=select1].ms-parent').click();

// 2nd select
await page.locator('div[data-test=select2].ms-parent').click();
const select2GroupElms = await page.locator('div[data-test=select2] li.group');
const select2li1 = await page.locator('div[data-test=select2] .ms-drop li.group').nth(0);
await expect(await select2li1.locator('label.optgroup')).toHaveText('Group 3');
const select2li2 = await page.locator('div[data-test=select2] .ms-drop li.group').nth(1);
await expect(await select2li2.locator('label.optgroup')).toHaveText('Group 2');
await expect(select2GroupElms).toHaveCount(3);
await page.locator('div[data-test=select2].ms-parent').click();

// 3rd select
await page.locator('div[data-test=select3].ms-parent').click();
const select3LiElms = await page.locator('div[data-test=select3] li[role="option"]');
await expect(select3LiElms).toHaveCount(12);
const select3li1 = await page.locator('div[data-test=select3] .ms-drop li').nth(0);
await expect(await select3li1.locator('label')).toHaveText('December');
const select3li2 = await page.locator('div[data-test=select3] .ms-drop li').nth(1);
await expect(await select3li2.locator('label')).toHaveText('November');
const select3li3 = await page.locator('div[data-test=select3] .ms-drop li').nth(2);
await expect(await select3li3.locator('label')).toHaveText('October');
await page.locator('div[data-test=select3].ms-parent').click();

// 4th select
await page.locator('div[data-test=select4].ms-parent').click();
const select4GroupElms = await page.locator('div[data-test=select4] li.group');
const select4li1 = await page.locator('div[data-test=select4] .ms-drop li.group').nth(0);
await expect(await select4li1.locator('label.optgroup')).toHaveText('Q4');
const select4li2 = await page.locator('div[data-test=select4] .ms-drop li.group').nth(1);
await expect(await select4li2.locator('label.optgroup')).toHaveText('Q3');
await expect(select4GroupElms).toHaveCount(4);
await page.locator('div[data-test=select4].ms-parent').click();
});
});
Loading