Skip to content

Commit

Permalink
feat(skeleton-loader): add ability to customize
Browse files Browse the repository at this point in the history
  • Loading branch information
SiTaggart committed Aug 27, 2021
1 parent 5ab4457 commit 2afd827
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 33 deletions.
6 changes: 6 additions & 0 deletions .changeset/smart-stingrays-sit.md
@@ -0,0 +1,6 @@
---
'@twilio-paste/skeleton-loader': patch
'@twilio-paste/core': patch
---

[Skeleton-loader] Enable Compoonent to respect element customizations set on the customization provider. Component now enables setting an element name on the underlying HTML element and checks the emotion theme object to determine whether it should merge in custom styles to the ones set by the component author.
@@ -1,24 +1,25 @@
import * as React from 'react';
import {matchers} from 'jest-emotion';
import {render} from '@testing-library/react';
import {render, screen} from '@testing-library/react';
// @ts-ignore typescript doesn't like js imports
import axe from '../../../../../.jest/axe-helper';
import {SkeletonLoader} from '../src';
import {Default} from '../stories/index.stories';
import {CustomizationProvider} from '@twilio-paste/customization';

expect.extend(matchers);

describe('SkeletonLoader', () => {
it('should render', () => {
const {getByTestId} = render(<Default />);
expect(getByTestId('default-skeleton')).toBeDefined();
expect(getByTestId('default-skeleton')).toHaveStyleRule('background-color', 'colorBackgroundStrong');
expect(getByTestId('default-skeleton')).toHaveStyleRule('border-radius', 'borderRadius20');
expect(getByTestId('default-skeleton')).toHaveStyleRule('height', 'sizeIcon20');
render(<Default />);
expect(screen.getByTestId('default-skeleton')).toBeDefined();
expect(screen.getByTestId('default-skeleton')).toHaveStyleRule('background-color', 'colorBackgroundStrong');
expect(screen.getByTestId('default-skeleton')).toHaveStyleRule('border-radius', 'borderRadius20');
expect(screen.getByTestId('default-skeleton')).toHaveStyleRule('height', 'sizeIcon20');
});

it('should render layout prop styles', (): void => {
const {getByTestId} = render(
render(
<Default
display="block"
width="size30"
Expand All @@ -30,51 +31,95 @@ describe('SkeletonLoader', () => {
size="size30"
/>
);
expect(getByTestId('default-skeleton')).toHaveStyleRule('display', 'block');
expect(getByTestId('default-skeleton')).toHaveStyleRule('width', 'size30');
expect(getByTestId('default-skeleton')).toHaveStyleRule('min-width', 'size30');
expect(getByTestId('default-skeleton')).toHaveStyleRule('max-width', 'size30');
expect(getByTestId('default-skeleton')).toHaveStyleRule('height', 'size30');
expect(getByTestId('default-skeleton')).toHaveStyleRule('min-height', 'size30');
expect(getByTestId('default-skeleton')).toHaveStyleRule('max-height', 'size30');
expect(screen.getByTestId('default-skeleton')).toHaveStyleRule('display', 'block');
expect(screen.getByTestId('default-skeleton')).toHaveStyleRule('width', 'size30');
expect(screen.getByTestId('default-skeleton')).toHaveStyleRule('min-width', 'size30');
expect(screen.getByTestId('default-skeleton')).toHaveStyleRule('max-width', 'size30');
expect(screen.getByTestId('default-skeleton')).toHaveStyleRule('height', 'size30');
expect(screen.getByTestId('default-skeleton')).toHaveStyleRule('min-height', 'size30');
expect(screen.getByTestId('default-skeleton')).toHaveStyleRule('max-height', 'size30');
});

it('should render size prop styles', (): void => {
const {getByTestId} = render(<Default size="size30" />);
expect(getByTestId('default-skeleton')).toHaveStyleRule('height', 'size30');
expect(getByTestId('default-skeleton')).toHaveStyleRule('width', 'size30');
render(<Default size="size30" />);
expect(screen.getByTestId('default-skeleton')).toHaveStyleRule('height', 'size30');
expect(screen.getByTestId('default-skeleton')).toHaveStyleRule('width', 'size30');
});

it('should render border-radius prop styles', (): void => {
const {getByTestId} = render(<Default borderRadius="borderRadiusCircle" />);
expect(getByTestId('default-skeleton')).toHaveStyleRule('border-radius', 'borderRadiusCircle');
render(<Default borderRadius="borderRadiusCircle" />);
expect(screen.getByTestId('default-skeleton')).toHaveStyleRule('border-radius', 'borderRadiusCircle');
});

it('should render top left border-radius prop styles', (): void => {
const {getByTestId} = render(<Default borderTopLeftRadius="borderRadiusCircle" />);
expect(getByTestId('default-skeleton')).toHaveStyleRule('border-top-left-radius', 'borderRadiusCircle');
render(<Default borderTopLeftRadius="borderRadiusCircle" />);
expect(screen.getByTestId('default-skeleton')).toHaveStyleRule('border-top-left-radius', 'borderRadiusCircle');
});

it('should render top right border-radius prop styles', (): void => {
const {getByTestId} = render(<Default borderTopRightRadius="borderRadiusCircle" />);
expect(getByTestId('default-skeleton')).toHaveStyleRule('border-top-right-radius', 'borderRadiusCircle');
render(<Default borderTopRightRadius="borderRadiusCircle" />);
expect(screen.getByTestId('default-skeleton')).toHaveStyleRule('border-top-right-radius', 'borderRadiusCircle');
});

it('should render bottom left border-radius prop styles', (): void => {
const {getByTestId} = render(<Default borderBottomLeftRadius="borderRadiusCircle" />);
expect(getByTestId('default-skeleton')).toHaveStyleRule('border-bottom-left-radius', 'borderRadiusCircle');
render(<Default borderBottomLeftRadius="borderRadiusCircle" />);
expect(screen.getByTestId('default-skeleton')).toHaveStyleRule('border-bottom-left-radius', 'borderRadiusCircle');
});

it('should render bottom right border-radius prop styles', (): void => {
const {getByTestId} = render(<Default borderBottomRightRadius="borderRadiusCircle" />);
expect(getByTestId('default-skeleton')).toHaveStyleRule('border-bottom-right-radius', 'borderRadiusCircle');
render(<Default borderBottomRightRadius="borderRadiusCircle" />);
expect(screen.getByTestId('default-skeleton')).toHaveStyleRule('border-bottom-right-radius', 'borderRadiusCircle');
});

describe('Customization', () => {
it('should have default DOM attribute present', () => {
render(<Default />);
expect(screen.getByTestId('default-skeleton').getAttribute('data-paste-element')).toEqual('SKELETON_LOADER');
});

it('should be able to create custom element dom attribute', () => {
render(<Default element="CUSTOM" />);
expect(screen.getByTestId('default-skeleton').getAttribute('data-paste-element')).toEqual('CUSTOM');
});

it('should apply custom style to default element name', () => {
render(
<CustomizationProvider
baseTheme="default"
elements={{
SKELETON_LOADER: {
margin: 'space80',
},
}}
>
<Default />
</CustomizationProvider>
);
expect(screen.getByTestId('default-skeleton')).toHaveStyleRule('margin', '1.75rem');
});

it('should apply custom style to a custom element name', () => {
render(
<CustomizationProvider
baseTheme="default"
elements={{
CUSTOM_SKELETON_LOADER: {
padding: 'space30',
},
}}
>
<Default element="CUSTOM_SKELETON_LOADER" />
</CustomizationProvider>
);
expect(screen.getByTestId('default-skeleton')).toHaveStyleRule('padding', '0.5rem');
});
});
});

describe('Accessibility', () => {
it('Should have no accessibility violations', async () => {
const {container} = render(<SkeletonLoader />);
const results = await axe(container);
expect(results).toHaveNoViolations();
describe('Accessibility', () => {
it('Should have no accessibility violations', async () => {
const {container} = render(<SkeletonLoader />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
});
});
4 changes: 4 additions & 0 deletions packages/paste-core/components/skeleton-loader/src/index.tsx
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react';
import {useSpring, animated} from '@twilio-paste/animation-library';
import {css, styled} from '@twilio-paste/styling-library';
import {Box, safelySpreadBoxProps} from '@twilio-paste/box';
import type {BoxElementProps} from '@twilio-paste/box';
import type {LayoutProps, BorderRadiusProps} from '@twilio-paste/style-props';

const AnimatedSkeleton = animated(Box);
Expand All @@ -14,6 +15,7 @@ const StyledAnimatedSkeleton = styled(AnimatedSkeleton)(() =>

export interface SkeletonLoaderProps
extends React.HTMLAttributes<HTMLDivElement>,
Pick<BoxElementProps, 'element'>,
Omit<LayoutProps, 'verticalAlign'>,
BorderRadiusProps {}

Expand All @@ -25,6 +27,7 @@ const SkeletonLoader = React.forwardRef<HTMLDivElement, SkeletonLoaderProps>(
borderRadius = 'borderRadius20',
borderTopLeftRadius,
borderTopRightRadius,
element = 'SKELETON_LOADER',
display,
height = 'sizeIcon20',
maxHeight,
Expand Down Expand Up @@ -59,6 +62,7 @@ const SkeletonLoader = React.forwardRef<HTMLDivElement, SkeletonLoaderProps>(
borderTopLeftRadius={borderTopLeftRadius}
borderTopRightRadius={borderTopRightRadius}
display={display}
element={element}
height={height}
maxHeight={maxHeight}
maxWidth={maxWidth}
Expand Down
Expand Up @@ -8,6 +8,8 @@ import {Heading} from '@twilio-paste/heading';
import {Paragraph} from '@twilio-paste/paragraph';
import {Stack} from '@twilio-paste/stack';
import {Table, THead, TBody, Tr, Td, Th} from '@twilio-paste/table';
import {CustomizationProvider} from '@twilio-paste/customization';
import {useTheme} from '@twilio-paste/theme';
import {Text} from '@twilio-paste/text';
import {CalendarIcon} from '@twilio-paste/icons/esm/CalendarIcon';
import type {SkeletonLoaderProps} from '../src';
Expand Down Expand Up @@ -324,3 +326,28 @@ export const TableLoading: React.FC = () => {
</>
);
};

export const CustomizedSkeletonLoader: React.FC = () => {
const activeTheme = useTheme();
return (
<CustomizationProvider
baseTheme="default"
theme={activeTheme}
elements={{
SKELETON_LOADER: {backgroundColor: 'colorBackgroundBrandHighlight', margin: 'space100'},
CUSTOM_SKELETON: {backgroundColor: 'colorBackgroundBrand', padding: 'space80'},
}}
>
<Stack orientation="vertical" spacing="space50">
<Heading as="h2" variant="heading20" marginBottom="space0">
Customized Skeleton
</Heading>
<SkeletonLoader data-testid="default-skeleton" />
<Heading as="h2" variant="heading20" marginBottom="space0">
Custom Skeleton
</Heading>
<SkeletonLoader element="CUSTOM_SKELETON" data-testid="default-skeleton" />
</Stack>
</CustomizationProvider>
);
};

0 comments on commit 2afd827

Please sign in to comment.