Skip to content

Commit

Permalink
feat: a11y/colors (#221)
Browse files Browse the repository at this point in the history
* feat: first pass at color checking

* test: add color tests
  • Loading branch information
nicolasbrugneaux committed Jun 3, 2022
1 parent c256b4f commit 3e36aed
Show file tree
Hide file tree
Showing 8 changed files with 326 additions and 64 deletions.
6 changes: 3 additions & 3 deletions packages/example/components/theme-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const defaultDark = {
const defaultLight = {
primary: '#6366f1',
secondary: '#eef2ff',
text: '#000000',
text: '#000',
textSecondary: '#1f2937',
textTertiary: '#64748b',
muted: '#e2e8f0',
Expand All @@ -74,7 +74,7 @@ const defaultLight = {
const greenCustom = {
primary: '#34d399',
secondary: '#ecfccb',
text: '#fff',
text: 'hsla(81, 88%, 80%)',
textSecondary: '#d9f99d',
textTertiary: '#bef264',
muted: '#3f6212',
Expand All @@ -89,7 +89,7 @@ const roseCustom = {
textTertiary: '#fecdd3',
muted: '#3f3f46',
background: '#27272a',
error: '#ef4444',
error: 'rgb(255, 0, 0)',
};

export const themes = [defaultDark, defaultLight, greenCustom, roseCustom];
28 changes: 10 additions & 18 deletions packages/react-celo/__tests__/react-celo-provider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Mainnet } from '../src/constants';
import { CeloProvider, CeloProviderProps } from '../src/react-celo-provider';
import { Maybe, Network, Theme } from '../src/types';
import { UseCelo, useCelo, useCeloInternal } from '../src/use-celo';
import defaultTheme from '../src/theme/default';

interface RenderArgs {
providerProps: Partial<CeloProviderProps>;
Expand Down Expand Up @@ -194,29 +195,20 @@ describe('CeloProvider', () => {
expect(result.current.network).toEqual(Mainnet);

act(() => {
result.current.updateTheme({
background: '#000',
primary: '#000',
secondary: '#000',
muted: '#000',
error: '#000',
text: '#000',
textSecondary: '#000',
textTertiary: '#000',
});
result.current.updateTheme(defaultTheme.light);
});

rerender();

expect(result.current.theme).toEqual({
background: '#000',
primary: '#000',
secondary: '#000',
muted: '#000',
error: '#000',
text: '#000',
textSecondary: '#000',
textTertiary: '#000',
background: '#ffffff',
primary: '#6366f1',
secondary: '#eef2ff',
muted: '#e2e8f0',
error: '#ef4444',
text: '#000000',
textSecondary: '#1f2937',
textTertiary: '#64748b',
});
});
});
Expand Down
102 changes: 102 additions & 0 deletions packages/react-celo/__tests__/utils/colors.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import {
Color,
contrast,
hexToRGB,
luminance,
RGBToHex,
} from '../../src/utils/colors';

describe('RGBToHex', () => {
it('converts rgb to hex', () => {
expect(RGBToHex('rgb(0, 0, 0)')).toEqual('#000000');
expect(RGBToHex('rgb(255, 0, 0)')).toEqual('#ff0000');
});
it('converts rgba to hex, with an error, and strip the alpha', () => {
const spy = jest.spyOn(console, 'error');
expect(RGBToHex('rgba(255, 255, 255, 0.5)')).toEqual('#ffffff80');
expect(spy).toBeCalled();
});
});

describe('hexToRGB', () => {
it('converts hex to rgb', () => {
expect(hexToRGB('#000000')).toEqual('rgb(0, 0, 0)');
expect(hexToRGB('#ff0000')).toEqual('rgb(255, 0, 0)');
});
it('converts hex to rgba', () => {
expect(hexToRGB('#000000ff')).toEqual('rgba(0, 0, 0, 1)');
expect(hexToRGB('#ff0000', 0.5)).toEqual('rgba(255, 0, 0, 0.5)');
});
});

describe('luminance', () => {
it('calculates the luminance of one color', () => {
expect(luminance(new Color('#fff'))).toBe(1);
expect(luminance(new Color('#000'))).toBe(0);
const randomColor = '#' + Math.floor(Math.random() * 0xffffff).toString(16);
expect(luminance(new Color(randomColor))).toBeGreaterThanOrEqual(0);
expect(luminance(new Color(randomColor))).toBeLessThanOrEqual(1);
});
});

describe('contrast', () => {
it('calculates the constrats between two colors', () => {
expect(contrast(new Color('#fff'), new Color('#000'))).toBe(21);
expect(contrast(new Color('#000'), new Color('#000'))).toBe(1);
expect(contrast(new Color('#fff'), new Color('#fff'))).toBe(1);
expect(contrast(new Color('#fff'), new Color('#444'))).toBe(9.74);
});
});

describe('Color', () => {
it('accepts hex', () => {
const color1 = new Color('#000000');
expect(color1.r).toEqual(0x00);
expect(color1.g).toEqual(0x00);
expect(color1.b).toEqual(0x00);
expect(color1.a).toEqual(null);
const color2 = new Color('#f00');
expect(color2.r).toEqual(0xff);
expect(color1.g).toEqual(0x00);
expect(color1.b).toEqual(0x00);
expect(color1.a).toEqual(null);
const color3 = new Color('#ffffff80');
expect(color3.r).toEqual(0xff);
expect(color3.r).toEqual(0xff);
expect(color3.r).toEqual(0xff);
expect(color3.a).toEqual(0.5);
});
it('accepts rgb(a)', () => {
const color1 = new Color('rgb(0, 0, 0)');
expect(color1.r).toEqual(0x00);
expect(color1.g).toEqual(0x00);
expect(color1.b).toEqual(0x00);
expect(color1.a).toEqual(null);
const color2 = new Color('rgb(255, 0, 0)');
expect(color2.r).toEqual(0xff);
expect(color1.g).toEqual(0x00);
expect(color1.b).toEqual(0x00);
expect(color1.a).toEqual(null);
const color3 = new Color('rgb(255, 255, 255, 0.2)');
expect(color3.r).toEqual(0xff);
expect(color3.r).toEqual(0xff);
expect(color3.r).toEqual(0xff);
expect(color3.a).toEqual(0.2);
});
it('accepts hsl(a)', () => {
const color1 = new Color('hsl(0, 0, 0)');
expect(color1.r).toEqual(0x00);
expect(color1.g).toEqual(0x00);
expect(color1.b).toEqual(0x00);
expect(color1.a).toEqual(null);
const color2 = new Color('hsl(359, 94, 62)');
expect(color2.r).toEqual(0xf9);
expect(color2.g).toEqual(0x43);
expect(color2.b).toEqual(0x46);
const color3 = new Color('hsl(359, 94, 62, 0.3)');
expect(color3.r).toEqual(0xf9);
expect(color3.g).toEqual(0x43);
expect(color3.b).toEqual(0x46);
expect(color3.a).toEqual(0.3);
});
});
2 changes: 1 addition & 1 deletion packages/react-celo/src/modals/action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import ReactModal from 'react-modal';
import Spinner from '../components/spinner';
import { Theme } from '../types';
import { useCeloInternal } from '../use-celo';
import { hexToRGB } from '../utils/helpers';
import { hexToRGB } from '../utils/colors';
import cls from '../utils/tailwind';
import useTheme from '../utils/useTheme';
import { styles as modalStyles } from './connect';
Expand Down
2 changes: 1 addition & 1 deletion packages/react-celo/src/modals/connect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
WalletEntry,
} from '../types';
import { useCeloInternal } from '../use-celo';
import { hexToRGB } from '../utils/helpers';
import { hexToRGB } from '../utils/colors';
import { defaultProviderSort, SortingPredicate } from '../utils/sort';
import cls from '../utils/tailwind';
import useProviders, { walletToProvider } from '../utils/useProviders';
Expand Down
28 changes: 9 additions & 19 deletions packages/react-celo/src/use-celo-methods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ import {
useContractsCache,
} from './ContractCacheBuilder';
import { Dispatcher } from './react-celo-provider';
import defaultTheme from './theme/default';
import { Connector, Network, Theme } from './types';
import { RGBToHex } from './utils/helpers';
import { contrastCheck, fixTheme } from './utils/colors';

export function useCeloMethods(
{
Expand Down Expand Up @@ -185,23 +184,14 @@ export function useCeloMethods(
const updateTheme = useCallback(
(theme: Theme | null) => {
if (!theme) return dispatch('setTheme', null);
Object.entries(theme).forEach(([key, value]: [string, string]) => {
if (!(key in defaultTheme.light)) {
console.warn(`Theme key ${key} is not valid.`);
}
const _key = key as keyof Theme;
if (value.startsWith('rgb')) {
theme[_key] = RGBToHex(value);
console.warn(
`RGB values not officially supported, but were translated to hex (${value} -> ${theme[_key]})`
);
} else if (!value.startsWith('#')) {
theme[_key] = `#${value}`;
console.warn(
`Malformed hex value was missing # (${value} -> ${theme[_key]})`
);
}
});

if (process.env.NODE_ENV !== 'production') {
fixTheme(theme);
// minimal recommended contrast ratio is 4.
// or 3 for larger font-sizes
contrastCheck(theme);
}

dispatch('setTheme', theme);
},
[dispatch]
Expand Down
Loading

0 comments on commit 3e36aed

Please sign in to comment.