|
1 | 1 | import { newSpecPage } from '@stencil/core/testing'; |
2 | 2 |
|
| 3 | +import { Buttons } from '../../components/buttons/buttons'; |
3 | 4 | import { CardContent } from '../../components/card-content/card-content'; |
4 | 5 | import { Chip } from '../../components/chip/chip'; |
5 | 6 | import { |
| 7 | + generateColorClasses, |
6 | 8 | generateComponentThemeCSS, |
7 | 9 | generateCSSVars, |
8 | 10 | generateGlobalThemeCSS, |
9 | 11 | getClassList, |
10 | 12 | getClassMap, |
11 | 13 | getCustomTheme, |
| 14 | + hexToRgb, |
12 | 15 | injectCSS, |
| 16 | + mix, |
13 | 17 | } from '../theme'; |
14 | 18 |
|
15 | 19 | describe('getClassList()', () => { |
@@ -281,6 +285,20 @@ describe('injectCSS', () => { |
281 | 285 |
|
282 | 286 | expect(shadowRoot!.innerHTML).toContain(`<style>${css}</style>`); |
283 | 287 | }); |
| 288 | + |
| 289 | + it('should inject CSS into a scoped element', async () => { |
| 290 | + const page = await newSpecPage({ |
| 291 | + components: [Buttons], |
| 292 | + html: '<ion-buttons></ion-buttons>', |
| 293 | + }); |
| 294 | + |
| 295 | + const target = page.body.querySelector('ion-buttons')!; |
| 296 | + |
| 297 | + const css = ':host { background-color: red; }'; |
| 298 | + injectCSS(css, target); |
| 299 | + |
| 300 | + expect(target.innerHTML).toContain(`<style>${css}</style>`); |
| 301 | + }); |
284 | 302 | }); |
285 | 303 |
|
286 | 304 | describe('generateGlobalThemeCSS', () => { |
@@ -560,3 +578,214 @@ describe('generateComponentThemeCSS', () => { |
560 | 578 | expect(css).toBe(expectedCSS); |
561 | 579 | }); |
562 | 580 | }); |
| 581 | + |
| 582 | +describe('generateColorClasses', () => { |
| 583 | + let consoleWarnSpy: jest.SpyInstance; |
| 584 | + |
| 585 | + beforeEach(() => { |
| 586 | + consoleWarnSpy = jest.spyOn(console, 'warn'); |
| 587 | + // Suppress console.warn output from polluting the test output |
| 588 | + consoleWarnSpy.mockImplementation(() => {}); |
| 589 | + }); |
| 590 | + |
| 591 | + afterEach(() => { |
| 592 | + consoleWarnSpy.mockRestore(); |
| 593 | + }); |
| 594 | + |
| 595 | + it('should generate color classes for a given theme', () => { |
| 596 | + const theme = { |
| 597 | + palette: { |
| 598 | + light: { |
| 599 | + color: { |
| 600 | + primary: { |
| 601 | + bold: { |
| 602 | + base: '#0054e9', |
| 603 | + contrast: '#ffffff', |
| 604 | + foreground: '#000000', |
| 605 | + shade: '#0041c4', |
| 606 | + tint: '#0065ff', |
| 607 | + }, |
| 608 | + subtle: { |
| 609 | + base: '#0054e9', |
| 610 | + contrast: '#ffffff', |
| 611 | + foreground: '#000000', |
| 612 | + shade: '#0041c4', |
| 613 | + tint: '#0065ff', |
| 614 | + }, |
| 615 | + }, |
| 616 | + }, |
| 617 | + }, |
| 618 | + }, |
| 619 | + }; |
| 620 | + |
| 621 | + const css = generateColorClasses(theme).replace(/\s/g, ''); |
| 622 | + |
| 623 | + const expectedCSS = ` |
| 624 | + :root .ion-color-primary { |
| 625 | + --ion-color-base: var(--ion-color-primary, var(--ion-color-primary-bold)) !important; |
| 626 | + --ion-color-base-rgb: var(--ion-color-primary-rgb, var(--ion-color-primary-bold-rgb)) !important; |
| 627 | + --ion-color-contrast: var(--ion-color-primary-contrast, var(--ion-color-primary-bold-contrast)) !important; |
| 628 | + --ion-color-contrast-rgb: var(--ion-color-primary-contrast-rgb, var(--ion-color-primary-bold-contrast-rgb)) !important; |
| 629 | + --ion-color-shade: var(--ion-color-primary-shade, var(--ion-color-primary-bold-shade)) !important; |
| 630 | + --ion-color-tint: var(--ion-color-primary-tint, var(--ion-color-primary-bold-tint)) !important; |
| 631 | + --ion-color-foreground: var(--ion-color-primary-foreground, var(--ion-color-primary-bold-foreground)) !important; |
| 632 | +
|
| 633 | + --ion-color-subtle-base: var(--ion-color-primary-subtle) !important; |
| 634 | + --ion-color-subtle-base-rgb: var(--ion-color-primary-subtle-rgb) !important; |
| 635 | + --ion-color-subtle-contrast: var(--ion-color-primary-subtle-contrast) !important; |
| 636 | + --ion-color-subtle-contrast-rgb: var(--ion-color-primary-subtle-contrast-rgb) !important; |
| 637 | + --ion-color-subtle-shade: var(--ion-color-primary-subtle-shade) !important; |
| 638 | + --ion-color-subtle-tint: var(--ion-color-primary-subtle-tint) !important; |
| 639 | + --ion-color-subtle-foreground: var(--ion-color-primary-subtle-foreground) !important; |
| 640 | + } |
| 641 | + `.replace(/\s/g, ''); |
| 642 | + |
| 643 | + expect(css).toBe(expectedCSS); |
| 644 | + }); |
| 645 | + |
| 646 | + it('should not generate color classes for a given theme without colors', () => { |
| 647 | + const theme = { |
| 648 | + spacing: { |
| 649 | + xs: '12px', |
| 650 | + sm: '12px', |
| 651 | + md: '12px', |
| 652 | + lg: '12px', |
| 653 | + xl: '12px', |
| 654 | + xxl: '12px', |
| 655 | + }, |
| 656 | + }; |
| 657 | + |
| 658 | + const css = generateColorClasses(theme).replace(/\s/g, ''); |
| 659 | + |
| 660 | + expect(css).toBe(''); |
| 661 | + }); |
| 662 | + |
| 663 | + it('should not generate color classes for a given theme with an invalid string color value', () => { |
| 664 | + const theme = { |
| 665 | + spacing: { |
| 666 | + xs: '12px', |
| 667 | + sm: '12px', |
| 668 | + md: '12px', |
| 669 | + lg: '12px', |
| 670 | + xl: '12px', |
| 671 | + xxl: '12px', |
| 672 | + }, |
| 673 | + color: 'red', |
| 674 | + }; |
| 675 | + |
| 676 | + const css = generateColorClasses(theme).replace(/\s/g, ''); |
| 677 | + |
| 678 | + // Only check the first log to get the string message |
| 679 | + expect(consoleWarnSpy.mock.calls[0][0]).toContain( |
| 680 | + '[Ionic Warning]: Invalid color configuration in theme. Expected color to be an object, but found string.' |
| 681 | + ); |
| 682 | + |
| 683 | + expect(css).toBe(''); |
| 684 | + }); |
| 685 | + |
| 686 | + it('should not generate color classes for a given theme with an invalid array color value', () => { |
| 687 | + const theme = { |
| 688 | + spacing: { |
| 689 | + xs: '12px', |
| 690 | + sm: '12px', |
| 691 | + md: '12px', |
| 692 | + lg: '12px', |
| 693 | + xl: '12px', |
| 694 | + xxl: '12px', |
| 695 | + }, |
| 696 | + color: ['red', 'blue', 'yellow'], |
| 697 | + }; |
| 698 | + |
| 699 | + const css = generateColorClasses(theme).replace(/\s/g, ''); |
| 700 | + |
| 701 | + // Only check the first log to get the string message |
| 702 | + expect(consoleWarnSpy.mock.calls[0][0]).toContain( |
| 703 | + '[Ionic Warning]: Invalid color configuration in theme. Expected color to be an object, but found array.' |
| 704 | + ); |
| 705 | + |
| 706 | + expect(css).toBe(''); |
| 707 | + }); |
| 708 | +}); |
| 709 | + |
| 710 | +describe('hexToRgb()', () => { |
| 711 | + it('should convert 6-digit hex colors to RGB strings', () => { |
| 712 | + expect(hexToRgb('#ffffff')).toBe('255, 255, 255'); |
| 713 | + expect(hexToRgb('#000000')).toBe('0, 0, 0'); |
| 714 | + expect(hexToRgb('#ff0000')).toBe('255, 0, 0'); |
| 715 | + expect(hexToRgb('#00ff00')).toBe('0, 255, 0'); |
| 716 | + expect(hexToRgb('#0000ff')).toBe('0, 0, 255'); |
| 717 | + expect(hexToRgb('#3880ff')).toBe('56, 128, 255'); |
| 718 | + }); |
| 719 | + |
| 720 | + it('should convert 3-digit hex colors to RGB strings', () => { |
| 721 | + expect(hexToRgb('#fff')).toBe('255, 255, 255'); |
| 722 | + expect(hexToRgb('#000')).toBe('0, 0, 0'); |
| 723 | + expect(hexToRgb('#f00')).toBe('255, 0, 0'); |
| 724 | + expect(hexToRgb('#0f0')).toBe('0, 255, 0'); |
| 725 | + expect(hexToRgb('#00f')).toBe('0, 0, 255'); |
| 726 | + expect(hexToRgb('#abc')).toBe('170, 187, 204'); |
| 727 | + }); |
| 728 | + |
| 729 | + it('should handle hex colors without hash prefix', () => { |
| 730 | + expect(hexToRgb('ffffff')).toBe('255, 255, 255'); |
| 731 | + expect(hexToRgb('fff')).toBe('255, 255, 255'); |
| 732 | + expect(hexToRgb('3880ff')).toBe('56, 128, 255'); |
| 733 | + }); |
| 734 | +}); |
| 735 | + |
| 736 | +describe('mix()', () => { |
| 737 | + it('should mix two hex colors by weight percentage', () => { |
| 738 | + // Mix white into black |
| 739 | + expect(mix('#000000', '#ffffff', '0%')).toBe('#000000'); |
| 740 | + expect(mix('#000000', '#ffffff', '50%')).toBe('#808080'); |
| 741 | + expect(mix('#000000', '#ffffff', '100%')).toBe('#ffffff'); |
| 742 | + }); |
| 743 | + |
| 744 | + it('should mix colors with different percentages', () => { |
| 745 | + // Mix red into blue |
| 746 | + expect(mix('#0000ff', '#ff0000', '25%')).toBe('#4000bf'); |
| 747 | + expect(mix('#0000ff', '#ff0000', '75%')).toBe('#bf0040'); |
| 748 | + }); |
| 749 | + |
| 750 | + it('should handle 3-digit hex colors', () => { |
| 751 | + expect(mix('#000', '#fff', '50%')).toBe('#808080'); |
| 752 | + expect(mix('#f00', '#0f0', '50%')).toBe('#808000'); |
| 753 | + |
| 754 | + // 3-digit + 6-digit |
| 755 | + expect(mix('#000', '#ffffff', '50%')).toBe('#808080'); |
| 756 | + expect(mix('#000000', '#fff', '50%')).toBe('#808080'); |
| 757 | + }); |
| 758 | + |
| 759 | + it('should handle hex colors without hash prefix', () => { |
| 760 | + expect(mix('000000', 'ffffff', '50%')).toBe('#808080'); |
| 761 | + expect(mix('f00', '0f0', '50%')).toBe('#808000'); |
| 762 | + |
| 763 | + // With and without hash prefix |
| 764 | + expect(mix('#000000', 'ffffff', '50%')).toBe('#808080'); |
| 765 | + expect(mix('f00', '#0f0', '50%')).toBe('#808000'); |
| 766 | + }); |
| 767 | + |
| 768 | + it('should handle fractional percentages', () => { |
| 769 | + expect(mix('#000000', '#ffffff', '12.5%')).toBe('#202020'); |
| 770 | + expect(mix('#ffffff', '#000000', '87.5%')).toBe('#202020'); |
| 771 | + }); |
| 772 | + |
| 773 | + it('should work with real-world color examples', () => { |
| 774 | + // Mix primary Ionic blue with white |
| 775 | + expect(mix('#3880ff', '#ffffff', '10%')).toBe('#4c8dff'); |
| 776 | + |
| 777 | + // Mix primary Ionic blue with black for shade |
| 778 | + expect(mix('#3880ff', '#000000', '12%')).toBe('#3171e0'); |
| 779 | + }); |
| 780 | + |
| 781 | + it('should handle edge cases', () => { |
| 782 | + // Same colors should return base color regardless of weight |
| 783 | + expect(mix('#ff0000', '#ff0000', '50%')).toBe('#ff0000'); |
| 784 | + |
| 785 | + // Zero weight should return base color |
| 786 | + expect(mix('#123456', '#abcdef', '0%')).toBe('#123456'); |
| 787 | + |
| 788 | + // 100% weight should return mix color |
| 789 | + expect(mix('#123456', '#abcdef', '100%')).toBe('#abcdef'); |
| 790 | + }); |
| 791 | +}); |
0 commit comments