Skip to content

Commit

Permalink
chore: add cssStyler to both regular and optgroup option list (#149)
Browse files Browse the repository at this point in the history
  • Loading branch information
ghiscoding authored Nov 6, 2023
1 parent 8ad2b28 commit 8bd346b
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 15 deletions.
24 changes: 24 additions & 0 deletions demo/src/options/options26.html
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,28 @@ <h2 class="bd-title">
</select>
</div>
</div>

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

<div class="col-sm-10">
<select id="group2" multiple="multiple" class="full-width" data-test="select4">
<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>
18 changes: 15 additions & 3 deletions demo/src/options/options26.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default class Example {
ms3?: MultipleSelectInstance;

mount() {
this.ms1 = multipleSelect('#basic', {
this.ms2 = multipleSelect('[data-test="select1"]', {
styler: (row: OptionRowData | OptGroupRowData) => {
if (+(row?.value ?? 0) === 1) {
return 'background-color: #ffee00; color: #ff0000;';
Expand All @@ -18,7 +18,7 @@ export default class Example {
},
}) as MultipleSelectInstance;

this.ms2 = multipleSelect('#group', {
this.ms2 = multipleSelect('[data-test="select2"]', {
styler: (row: OptionRowData | OptGroupRowData) => {
if (row?.type === 'optgroup') {
return 'color: #777; font-weight: normal;';
Expand All @@ -30,7 +30,7 @@ export default class Example {
},
}) as MultipleSelectInstance;

this.ms3 = multipleSelect('#select3', {
this.ms2 = multipleSelect('[data-test="select3"]', {
cssStyler: (row: OptionRowData | OptGroupRowData) => {
if (+(row?.value ?? 0) === 2) {
return { backgroundColor: '#6fbeff', color: '#0014ff', fontStyle: 'italic' } as CSSStyleDeclaration;
Expand All @@ -41,6 +41,18 @@ export default class Example {
return null;
},
}) as MultipleSelectInstance;

this.ms2 = multipleSelect('[data-test="select4"]', {
cssStyler: (row: OptionRowData | OptGroupRowData) => {
if (row?.type === 'optgroup') {
return { color: '#787878', fontWeight: 'normal' } as CSSStyleDeclaration;
}
if (+(row?.value ?? 0) === 3) {
return { color: 'purple', textDecoration: 'underline' } as CSSStyleDeclaration;
}
return null;
},
}) as MultipleSelectInstance;
}

unmount() {
Expand Down
15 changes: 7 additions & 8 deletions lib/src/MultipleSelectInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import Constants from './constants';
import { compareObjects, deepCopy, findByParam, removeDiacritics, removeUndefined, setDataKeys, stripScripts } from './utils';
import {
applyCssRules,
applyParsedStyleToElement,
calculateAvailableSpace,
createDomElement,
Expand All @@ -15,7 +16,7 @@ import {
toggleElement,
} from './utils/domUtils';
import type { HtmlElementPosition } from './utils/domUtils';
import type { CSSStyleDeclarationWritable, MultipleSelectOption } from './interfaces/multipleSelectOption.interface';
import type { MultipleSelectOption } from './interfaces/multipleSelectOption.interface';
import type { MultipleSelectLocales, OptGroupRowData, OptionDataObject, OptionRowData } from './interfaces';
import { BindingEventService, VirtualScroll } from './services';

Expand Down Expand Up @@ -516,6 +517,7 @@ export class MultipleSelectInstance {
}

if (row.type === 'optgroup') {
const customStyleRules = this.options.cssStyler(row);
const customStyle = this.options.styler(row);
const styleStr = String(customStyle || '');
const htmlElms: HTMLElement[] = [];
Expand Down Expand Up @@ -544,6 +546,7 @@ export class MultipleSelectInstance {
labelElm.appendChild(spanElm);
const liElm = createDomElement('li', { className: `group ${classes}`.trim() });
applyParsedStyleToElement(liElm, styleStr);
applyCssRules(liElm, customStyleRules);
liElm.appendChild(labelElm);
htmlElms.push(liElm);

Expand All @@ -554,6 +557,7 @@ export class MultipleSelectInstance {
return htmlElms;
}

const customStyleRules = this.options.cssStyler(row);
const customStyle = this.options.styler(row);
const style = String(customStyle || '');
classes += row.classes || '';
Expand All @@ -574,14 +578,9 @@ export class MultipleSelectInstance {
if (title) {
liElm.title = title;
}
applyParsedStyleToElement(liElm, style);

const customStyleCss = this.options.cssStyler?.(row);
if (customStyleCss) {
for (const styleProp of Object.keys(customStyleCss)) {
liElm.style[styleProp as CSSStyleDeclarationWritable] = customStyleCss[styleProp as CSSStyleDeclarationWritable];
}
}
applyParsedStyleToElement(liElm, style);
applyCssRules(liElm, customStyleRules);
const labelClasses = `${row.disabled ? 'disabled' : ''}`;
const labelElm = document.createElement('label');
if (labelClasses) {
Expand Down
1 change: 1 addition & 0 deletions lib/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const DEFAULTS: Partial<MultipleSelectOption> = {
useSelectOptionLabel: false,
useSelectOptionLabelToHtml: false,

cssStyler: () => null,
styler: () => false,
textTemplate: (elm: HTMLOptionElement) => elm.innerHTML.trim(),
labelTemplate: (elm: HTMLOptionElement) => elm.label,
Expand Down
2 changes: 1 addition & 1 deletion lib/src/interfaces/multipleSelectOption.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ export interface MultipleSelectOption extends MultipleSelectLocale {
customFilter(options: LabelFilter | TextFilter): boolean;

/** The item styler function, return style string to customize the item style such as background: red. The function take one parameter: value. */
cssStyler?: (value: OptionRowData | OptGroupRowData) => CSSStyleDeclaration | null;
cssStyler: (value: OptionRowData | OptGroupRowData) => CSSStyleDeclaration | null;

/** @deprecated @use `cssStyler`. The item styler function, return style string to customize the item style such as background: red. The function take one parameter: value. */
styler: (value: OptionRowData | OptGroupRowData) => string | boolean | null;
Expand Down
9 changes: 9 additions & 0 deletions lib/src/utils/domUtils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CSSStyleDeclarationWritable } from '../interfaces';
import { toCamelCase } from './utils';

export type InferType<T> = T extends infer R ? R : any;
Expand All @@ -13,6 +14,14 @@ export interface HtmlElementPosition {
right: number;
}

export function applyCssRules(elm: HTMLElement, rules: CSSStyleDeclaration | null) {
if (elm && rules) {
for (const styleProp of Object.keys(rules)) {
elm.style[styleProp as CSSStyleDeclarationWritable] = rules[styleProp as CSSStyleDeclarationWritable];
}
}
}

export function applyParsedStyleToElement(elm: HTMLElement, styleStr: string) {
if (styleStr) {
const cstyles = styleStr.replace(/\s/g, '').split(';');
Expand Down
17 changes: 14 additions & 3 deletions playwright/e2e/options26.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ test.describe('Options 26 - The Styler / CSS Styler', () => {
await page.goto('#/options26');
});

test('first select has January & June with custom styling', async ({ page }) => {
test('1st select has January & June with custom styling', async ({ page }) => {
await page.locator('[data-test=select1].ms-parent').click();
const optionLoc1 = await page.locator('[data-test=select1] .ms-drop ul li').nth(0);
optionLoc1.click();
Expand All @@ -22,7 +22,7 @@ test.describe('Options 26 - The Styler / CSS Styler', () => {
await selectedText1.waitFor();
});

test('second select has January & June with custom styling', async ({ page }) => {
test('2nd select has January & June with custom styling', async ({ page }) => {
await page.locator('[data-test=select2].ms-parent').click();
const optionLoc1 = await page.locator('[data-test=select2] .ms-drop ul li').nth(1);
optionLoc1.click();
Expand All @@ -33,7 +33,7 @@ test.describe('Options 26 - The Styler / CSS Styler', () => {
await dropLoc2.waitFor();
});

test('third select has February & April with custom CSS styler', async ({ page }) => {
test('3rd select has February & April with custom CSS styler', async ({ page }) => {
await page.locator('[data-test=select3].ms-parent').click();
const optionLoc2 = await page.locator('[data-test=select3] .ms-drop ul li').nth(1);
optionLoc2.click();
Expand All @@ -49,4 +49,15 @@ test.describe('Options 26 - The Styler / CSS Styler', () => {
const selectedText3 = page.locator('[data-test=select3] .ms-choice span', { hasText: 'February, April' });
await selectedText3.waitFor();
});

test('4th select has January & June with custom styling', async ({ page }) => {
await page.locator('[data-test=select4].ms-parent').click();
const optionLoc3 = await page.locator('[data-test=select4] .ms-drop ul li').nth(3);
optionLoc3.click();
expect(optionLoc3).toHaveText('Option 3');
await expect(optionLoc3).toHaveCSS('color', 'rgb(128, 0, 128)');
await expect(optionLoc3).toHaveCSS('text-decoration', 'underline solid rgb(128, 0, 128)');
const dropLoc2 = await page.locator('[data-test=select4] .ms-choice span', { hasText: '[Group 1: Option 3]' });
await dropLoc2.waitFor();
});
});

0 comments on commit 8bd346b

Please sign in to comment.