@lifecorner/angular-ui-kit 3.27.1 → 3.27.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1790,3 +1790,105 @@ describe('Component pairings of 3.25.0 (request 039)', () => {
1790
1790
  });
1791
1791
  });
1792
1792
  });
1793
+
1794
+ /**
1795
+ * Request 044 (3.27.2, F-31, design 044 §5): the filled select, textarea and
1796
+ * datepicker draw the field boundary of the outline variant, --color-field-border
1797
+ * at rest and --color-text-secondary on hover (it was transparent at rest and
1798
+ * --color-border, 1.23 / 1.29, or nothing on hover). Pages are the background,
1799
+ * surface and surface-2; the field's own fill (--color-surface-sunken at rest,
1800
+ * --color-surface-hover on hover) is composited over each page.
1801
+ */
1802
+ describe('Component pairings of 3.27.2 (request 044)', () => {
1803
+ const lib = (path: string) => resolve(__dirname, '../lib', path);
1804
+ const fields = {
1805
+ select: {
1806
+ css: compile(lib('select/select.component.scss')).css,
1807
+ rest: '.lc-select.lc-select--filled',
1808
+ hover: '.lc-select.lc-select--filled:hover:not(.lc-select--disabled):not(.lc-select--error):not(:focus)',
1809
+ error: '.lc-select.lc-select--error',
1810
+ },
1811
+ textarea: {
1812
+ css: compile(lib('textarea/textarea.component.scss')).css,
1813
+ rest: '.lc-textarea--filled',
1814
+ hover: '.lc-textarea--filled:hover:not(:disabled):not(:read-only):not(.lc-textarea--error):not(:focus)',
1815
+ error: '.lc-textarea--error',
1816
+ },
1817
+ datepicker: {
1818
+ css: compile(lib('datepicker/datepicker.component.scss')).css,
1819
+ rest: '.lc-datepicker.lc-datepicker--filled',
1820
+ hover: '.lc-datepicker.lc-datepicker--filled:hover:not(:disabled):not([readonly]):not(.lc-datepicker--error):not(:focus)',
1821
+ error: '.lc-datepicker.lc-datepicker--filled.lc-datepicker--error',
1822
+ },
1823
+ };
1824
+ type Field = keyof typeof fields;
1825
+ const names = Object.keys(fields) as Field[];
1826
+
1827
+ const forcedBlock = /@media \(forced-colors: active\) \{((?:[^{}]*\{[^{}]*\})*)\s*\}/g;
1828
+ const rulesOf = (css: string) =>
1829
+ [...css.replace(forcedBlock, '').matchAll(/([^{}]+)\{([^{}]*)\}/g)].map((m) => ({
1830
+ selectors: m[1].split(/,(?![^(]*\))/).map((s) => s.trim()),
1831
+ body: m[2],
1832
+ }));
1833
+ const decls = (body: string): Record<string, string> =>
1834
+ Object.fromEntries([...body.matchAll(/([\w-]+):\s*([^;]+);/g)].map((m) => [m[1], m[2].replace(/\s+/g, ' ').trim()]));
1835
+ /** Every rule naming `selector`, merged in source order, forced-colours blocks left out. */
1836
+ const fills = (field: Field, selector: string): Record<string, string> => {
1837
+ const found = rulesOf(fields[field].css).filter((r) => r.selectors.includes(selector));
1838
+ expect([field, selector, found.length > 0]).toEqual([field, selector, true]);
1839
+ return Object.assign({}, ...found.map((r) => decls(r.body)));
1840
+ };
1841
+ const token = (value: string) => value.match(/^var\((--[\w-]+)\)$/)?.[1] ?? `not a token: ${value}`;
1842
+ const floor = (n: number) => Math.floor(n * 100) / 100;
1843
+
1844
+ it.each(names)('AC-8, AC-9: filled %s: field border on the sunken fill at rest, text-secondary on the hover fill', (field) => {
1845
+ const rest = fills(field, fields[field].rest);
1846
+ const hover = fills(field, fields[field].hover);
1847
+ expect([rest['background-color'], rest['border-color']]).toEqual(['var(--color-surface-sunken)', 'var(--color-field-border)']);
1848
+ expect([hover['background-color'], hover['border-color']]).toEqual(['var(--color-surface-hover)', 'var(--color-text-secondary)']);
1849
+ expect(fills(field, fields[field].error)['border-color']).toBe('var(--color-error-600)');
1850
+ });
1851
+
1852
+ // [light, dark] per page (background, surface, surface-2), design 044 §5.
1853
+ const expected = {
1854
+ restOnPage: [[4.83, 4.83, 4.62], [7.31, 6.7, 5.56]],
1855
+ restOnFill: [[4.39, 4.39, 4.39], [7.02, 7.02, 7.02]],
1856
+ hoverOnPage: [[7.55, 7.55, 7.23], [12.16, 11.15, 9.25]],
1857
+ hoverOnFill: [[6.86, 6.86, 6.86], [11.05, 9.97, 8.17]],
1858
+ // §5 quotes the page (background) only: 8.39 light, 9.47 dark.
1859
+ errorOnPage: [[8.39, 8.39, 8.02], [9.47, 8.68, 7.21]],
1860
+ errorOnFill: [[7.62, 7.62, 7.62], [9.09, 9.09, 9.09]],
1861
+ };
1862
+
1863
+ describe.each(palettes.map(([name, tokens], i) => [name, tokens, i] as const))('%s palette', (_palette, tokens, i) => {
1864
+ const c = (name: string) => color(tokens, name);
1865
+
1866
+ function measure(field: Field) {
1867
+ const pages = ['--color-background', '--color-surface', '--color-surface-2'].map(c);
1868
+ const rest = fills(field, fields[field].rest);
1869
+ const hover = fills(field, fields[field].hover);
1870
+ const error = fills(field, fields[field].error);
1871
+ const on = (ink: string, bases: Rgba[]) => bases.map((b) => floor(ratio(over(c(token(ink)), b), b)));
1872
+ const restFill = pages.map((p) => over(c(token(rest['background-color'])), p));
1873
+ const hoverFill = pages.map((p) => over(c(token(hover['background-color'])), p));
1874
+ return {
1875
+ restOnPage: on(rest['border-color'], pages),
1876
+ restOnFill: on(rest['border-color'], restFill),
1877
+ hoverOnPage: on(hover['border-color'], pages),
1878
+ hoverOnFill: on(hover['border-color'], hoverFill),
1879
+ errorOnPage: on(error['border-color'], pages),
1880
+ errorOnFill: on(error['border-color'], restFill),
1881
+ };
1882
+ }
1883
+
1884
+ it.each(names)('AC-8, AC-9: filled %s: the design 044 §5 ratios, all ≥ 3:1, hover ≥ rest', (field) => {
1885
+ const m = measure(field);
1886
+ for (const key of Object.keys(expected) as (keyof typeof expected)[]) {
1887
+ expect([key, m[key]]).toEqual([key, expected[key][i]]);
1888
+ expect(Math.min(...m[key])).toBeGreaterThanOrEqual(3);
1889
+ }
1890
+ m.hoverOnPage.forEach((r, page) => expect(r).toBeGreaterThanOrEqual(m.restOnPage[page]));
1891
+ m.hoverOnFill.forEach((r, page) => expect(r).toBeGreaterThanOrEqual(m.restOnFill[page]));
1892
+ });
1893
+ });
1894
+ });
@@ -181,7 +181,10 @@ describe('Touch targets, hover borders and contrast (request 043)', () => {
181
181
  .filter((r) => r.selectors.some((s) => field.test(s) && s.includes(':hover')))
182
182
  .map((r) => decls(r.body)['border-color'])
183
183
  .filter(Boolean);
184
- expect(borders.every((b) => b === 'var(--color-text-secondary)' || b === 'var(--color-border)')).toBe(true);
184
+ // Request 044 (F-31): --color-border (1.23:1 light) left the allow-list;
185
+ // the filled hovers moved to --color-text-secondary as well.
186
+ expect(borders.length).toBeGreaterThan(0);
187
+ expect(borders.every((b) => b === 'var(--color-text-secondary)')).toBe(true);
185
188
  });
186
189
 
187
190
  it('AC-3: the outline hover border (--color-text-secondary) is ≥ 3:1 on the page, the surface and surface-2', () => {