Skip to content
Permalink
Browse files

feat(website): Add text color token accessibility rating to the token…

… page (#28)

* feat(website): add accessibility rating for text color tokens

* feat(website): fix up the color rating test

* fix(website): color combos test prettier

* feat(screen-reader-only): create a SRO component

* fix: update the component category in package.json

* fix: \correct the tokens that are being displayed

* fix: remove importants and update license field

* fix: eslint of jest test promises
  • Loading branch information...
SiTaggart committed Aug 13, 2019
1 parent 3fbd4d0 commit 74f12d1aa94a28ecb1bd42f1391c093aeed4461a
@@ -0,0 +1,41 @@
{
"name": "@twilio-paste/screen-reader-only",
"version": "0.0.0",
"category": "typography",
"status": "alpha",
"description": "",
"author": "Twilio Inc.",
"license": "MIT",
"main:dev": "src/index.tsx",
"main": "dist/index.js",
"module": "dist/index.es.js",
"types": "dist/index.d.ts",
"sideEffects": false,
"files": [
"dist"
],
"scripts": {
"build": "yarn clean && yarn compile",
"build:dev": "yarn clean && yarn compile:dev",
"clean": "rm -rf ./dist && rm -rf tsconfig.build.tsbuildinfo && rm -rf .rpt2_cache",
"compile": "rollup -c --environment NODE_ENV:production",
"compile:dev": "rollup -c --environment NODE_ENV:development",
"prepublishOnly": "yarn build",
"type-check": "tsc --noEmit"
},
"peerDependencies": {
"@emotion/styled": "^10.0.10",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"styled-system": "^4.1.0"
},
"devDependencies": {
"rollup": "^1.16.2",
"rollup-plugin-babel": "^4.3.3",
"rollup-plugin-commonjs": "^10.0.1",
"rollup-plugin-node-resolve": "^5.1.0",
"rollup-plugin-terser": "^5.0.0",
"rollup-plugin-typescript2": "^0.21.2",
"typescript": "^3.5.2"
}
}
@@ -0,0 +1,34 @@
import typescript from 'rollup-plugin-typescript2';
import babel from 'rollup-plugin-babel';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import {terser} from 'rollup-plugin-terser';
import pkg from './package.json';

export default {
input: pkg['main:dev'],
output: [
{
file: pkg.main,
format: 'cjs',
},
{
file: pkg.module,
format: 'es',
},
],
external: [...Object.keys(pkg.peerDependencies || {})],
plugins: [
resolve(),
commonjs(),
typescript({
clean: true,
typescript: require('typescript'),
tsconfig: './tsconfig.build.json',
}),
babel({
exclude: 'node_modules/**',
}),
process.env.NODE_ENV === 'production' ? terser() : null,
],
};
@@ -0,0 +1,14 @@
import styled from '@emotion/styled';

export const ScreenReaderOnly = styled.span<{}>`
position: absolute;
margin: -1px;
border: 0;
padding: 0;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
text-transform: none;
white-space: nowrap;
`;
@@ -0,0 +1,15 @@
import * as React from 'react';
import {storiesOf} from '@storybook/react';
import {withKnobs} from '@storybook/addon-knobs';
import {Text} from '@twilio-paste/text';
import {ScreenReaderOnly} from '../src';

storiesOf('Utilities|Screen reader only', module)
.addDecorator(withKnobs)
.add('Default', () => {
return (
<Text>
Some visible text <ScreenReaderOnly>some not visible text</ScreenReaderOnly>
</Text>
);
});
@@ -0,0 +1,15 @@
{
"extends": "../../../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": [
"src/**/*"
],
"references": [
{
"path": "../../../paste-theme-tokens"
}
]
}
@@ -0,0 +1,4 @@
{
"extends": "../../../../tsconfig.json",
"include": ["src/**/*"]
}
@@ -4,8 +4,8 @@ import {dTSTokenFormat} from '../d.ts';
theo.registerFormat('d.ts', dTSTokenFormat);

describe('dTSFormatter', () => {
test('should return typescript definitions formatted tokens', () => {
theo
it('should return typescript definitions formatted tokens', () => {
return theo
.convert({
transform: {
type: 'web',
@@ -4,8 +4,8 @@ import {es6TokenFormat} from '../es6';
theo.registerFormat('es6.js', es6TokenFormat);

describe('es6Formatter', () => {
test('should return es6 formatted tokens', () => {
theo
it('should return es6 formatted tokens', () => {
return theo
.convert({
transform: {
type: 'web',
@@ -4,8 +4,8 @@ import {sketchpaletteTokenFormat} from '../sketchpalette';
theo.registerFormat('sketchpalette', sketchpaletteTokenFormat);

describe('sketchPaletteTokenFormatter', () => {
test('should return sketch palette color formatted tokens', () => {
theo
it('should return sketch palette color formatted tokens', () => {
return theo
.convert({
transform: {
type: 'web',
@@ -1,3 +1,2 @@
declare module 'gulp-theo';
declare module 'gulp-restart';
declare module 'color';
@@ -65,8 +65,12 @@ module.exports = {
return 'PasteUtility';
}

if (node.relativePath.endsWith('gatsby.json')) {
return 'PasteToken';
if (node.relativePath.endsWith('dist/tokens.gatsby.json')) {
return 'PasteTokenDefault';
}

if (node.relativePath.endsWith('dist/themes/sendgrid/tokens.gatsby.json')) {
return 'PasteTokenSendGrid';
}

return 'DefaultJson';
@@ -44,7 +44,10 @@ export const Td = styled.td(props => ({
},
}));

export const Th = styled(Td)(props => ({
interface ThProps {
width?: string;
}
export const Th = styled<ThProps>(Td)(props => ({
textAlign: 'left',
fontWeight: themeGet('fontWeights.fontWeightSemibold')(props),
})).withComponent('th');
@@ -2,8 +2,12 @@ import * as React from 'react';
import * as lodash from 'lodash';
import {ThemeShape} from '@twilio-paste/theme-tokens';
import {Absolute, AbsoluteProps} from '@twilio-paste/absolute';
import {useTheme} from '@twilio-paste/theme';
import {Box, BoxProps} from '@twilio-paste/box';
import {Text, TextProps} from '@twilio-paste/text';
import {ScreenReaderOnly} from '@twilio-paste/screen-reader-only';
import ColorCombos, {ColorCombinationAccessibility} from '../../utils/color-combos';
import colorRating from '../../utils/color-rating';

// Traditional import as the color package isn't exported and typed correctly
const Color = require('color');
@@ -20,13 +24,34 @@ export const BorderBox: React.FC<BorderBoxProps> = ({borderColor, borderWidth})
);
};

type TextBoxProp = Pick<TextProps, 'fontFamily' | 'fontSize' | 'fontWeight' | 'textColor'>;
interface TextBoxProps extends TextBoxProp {
color?: string;
type TextBoxProp = Pick<TextProps, 'fontFamily' | 'fontSize' | 'fontWeight'>;
export const TextBox: React.FC<TextBoxProp> = ({fontFamily, fontSize, fontWeight}) => {
return (
<Text fontFamily={fontFamily} fontSize={fontSize || 'fontSize60'} fontWeight={fontWeight} lineHeight="lineHeight60">
Ag
</Text>
);
};

type TextColorBoxProp = Pick<TextProps, 'textColor'>;
interface TextColorBoxProps extends TextColorBoxProp {
color: string;
}
export const TextBox: React.FC<TextBoxProps> = ({color, fontFamily, fontSize, fontWeight, textColor}) => {
export const TextColorBox: React.FC<TextColorBoxProps> = ({color, textColor}) => {
const theme = useTheme();
const colorFn = Color(color);
const isInverse = colorFn.isLight();
const backgroundColorValue = isInverse
? theme.backgroundColors.colorBackgroundBrand
: theme.backgroundColors.colorBackgroundBody;
const colorCombos = ColorCombos([color, backgroundColorValue]);
const {accessibility} = colorCombos[1].combinations[0];

const getContrastRating = (acc: ColorCombinationAccessibility): string => {
const rating = colorRating(acc);
return rating.small;
};

return (
<Absolute
backgroundColor={isInverse ? 'colorBackgroundBrand' : 'colorBackgroundBody'}
@@ -35,16 +60,15 @@ export const TextBox: React.FC<TextBoxProps> = ({color, fontFamily, fontSize, fo
css={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}}
>
<Text
fontFamily={fontFamily}
fontSize={fontSize || 'fontSize60'}
fontWeight={fontWeight}
lineHeight="lineHeight60"
textColor={textColor}
>
Ag
<Text as="span" fontSize="fontSize60" lineHeight="lineHeight60" textColor={textColor}>
<ScreenReaderOnly>Example text: </ScreenReaderOnly>Ag
</Text>
<Text as="span" fontSize="fontSize40" lineHeight="lineHeight30" textColor={textColor}>
<ScreenReaderOnly>Accessibility rating: </ScreenReaderOnly>
{getContrastRating(accessibility)}
</Text>
</Absolute>
);
@@ -107,7 +131,7 @@ export const TokenExample: React.FC<TokenExampleProps> = ({token}) => {
case 'spacing':
return <SpacingBox padding={tokenName as keyof ThemeShape['space']} />;
case 'text-color':
return <TextBox color={token.value} textColor={tokenName as keyof ThemeShape['textColors']} />;
return <TextColorBox color={token.value} textColor={tokenName as keyof ThemeShape['textColors']} />;
default:
return <Box>{token.value}</Box>;
}
@@ -29,31 +29,32 @@ interface TokenCategory {
tokens: Token[];
}

interface DataShape {
interface TokensShape {
node: {
tokens: TokenCategory[];
};
}

interface TokensListProps {
children?: React.ReactElement;
data: DataShape[];
default: TokensShape[];
sendgrid: TokensShape[];
}

const setInitialState = (data: DataShape[]): TokenCategory[] | null => {
const setInitialState = (data: TokensShape[]): TokenCategory[] | null => {
if (data != null) {
// eslint-disable-next-line prefer-destructuring
return data[0].node.tokens;
const {tokens} = data[0].node;
return tokens;
}
return null;
};

export const TokensList: React.FC<TokensListProps> = props => {
const [tokens, setTokens] = React.useState(setInitialState(props.data));
const [tokens, setTokens] = React.useState(setInitialState(props.sendgrid));

const filterTokenList = (filter: string): void => {
setTokens(() => {
const newTokenCategories = props.data[0].node.tokens.map(
const newTokenCategories = props.sendgrid[0].node.tokens.map(
(category): TokenCategory => {
const newTokens = category.tokens.filter(token => {
return token.name.includes(filter) || token.value.includes(filter);
@@ -105,22 +106,21 @@ export const TokensList: React.FC<TokensListProps> = props => {
<thead>
<Tr>
<Th>Token</Th>
<Th>Value</Th>
<Th>Description</Th>
<Th>Example</Th>
<Th width="250px">Value</Th>
<Th width="250px">Example</Th>
</Tr>
</thead>
<Tbody>
{cat.tokens.map((token: Token) => {
return (
<Tr key={`token${token.name}`}>
<Td>
<Text fontSize="fontSize30">
<Text fontSize="fontSize30" mb="space30">
<code>${token.name}</code>
</Text>
<Text textColor="colorTextWeak">{token.comment}</Text>
</Td>
<Td>{token.value}</Td>
<Td>{token.comment}</Td>
<Td
css={{
position: 'relative',
@@ -13,9 +13,9 @@ import {TokensList} from '../../components/tokens-list';

Laboris Lorem veniam nostrud enim do esse mollit adipisicing dolore proident labore elit et. Adipisicing sint incididunt ipsum nisi ut mollit et id est consequat qui cillum magna enim. Occaecat voluptate aute nisi aute magna enim minim quis. Irure minim nisi culpa ipsum nisi duis enim laboris officia occaecat laborum est. Duis reprehenderit magna eu laboris. Enim ad id nulla esse do excepteur.

<Box backgroundColor="colorBackground" borderRadius="borderRadius20" padding="space60" my="space100" maxWidth="size40">
<Text as="p" marginBottom="space40">
Working on Sketch? Download it from here. No access?
<Box backgroundColor="colorBackground" borderRadius="borderRadius20" padding="space80" my="space100" maxWidth="size40">
<Text as="p" lineHeight="lineHeight30" marginBottom="space40">
Working on Sketch? Download it from here. No access?{' '}
<Anchor href="#">
Send us a request
</Anchor>.
@@ -28,11 +28,29 @@ Laboris Lorem veniam nostrud enim do esse mollit adipisicing dolore proident lab
</Text>
</Box>

<TokensList data={props.data.allPasteToken.edges} />
<TokensList default={props.data.allPasteTokenDefault.edges} sendgrid={props.data.allPasteTokenSendGrid.edges} />

export const pageQuery = graphql`
{
allPasteToken {
allPasteTokenDefault {
edges {
node {
id
tokens {
categoryName
tokens {
category
comment
name
originalValue
type
value
}
}
}
}
},
allPasteTokenSendGrid {
edges {
node {
id

0 comments on commit 74f12d1

Please sign in to comment.
You can’t perform that action at this time.