Skip to content

Commit

Permalink
feat(icons): enable customization (#1839)
Browse files Browse the repository at this point in the history
* feat(icons): enable customization

* chore: alert snapshots

* fix: story
  • Loading branch information
SiTaggart committed Sep 3, 2021
1 parent adad8c5 commit c59aa86
Show file tree
Hide file tree
Showing 155 changed files with 1,043 additions and 294 deletions.
5 changes: 5 additions & 0 deletions .changeset/plenty-drinks-shave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@twilio-paste/icons': minor
---

[Icons] enable all icons to respect customization by giving them a generic element name, and ability to create custom element names.
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ exports[`Alert Variant error Should render an error alert 1`] = `
<span
className="emotion-0"
color="colorTextError"
data-paste-element="BOX"
data-paste-element="ICON"
display="block"
size="sizeIcon20"
>
Expand Down Expand Up @@ -267,7 +267,7 @@ exports[`Alert Variant error Should render an error alert with dismiss button 1`
<span
className="emotion-0"
color="colorTextError"
data-paste-element="BOX"
data-paste-element="ICON"
display="block"
size="sizeIcon20"
>
Expand Down Expand Up @@ -331,7 +331,7 @@ exports[`Alert Variant error Should render an error alert with dismiss button 1`
<span
className="emotion-3"
color="colorTextIcon"
data-paste-element="BOX"
data-paste-element="ICON"
display="block"
size="sizeIcon20"
>
Expand Down Expand Up @@ -441,7 +441,7 @@ exports[`Alert Variant neutral Should render a neutral alert 1`] = `
<span
className="emotion-0"
color="colorTextNeutral"
data-paste-element="BOX"
data-paste-element="ICON"
display="block"
size="sizeIcon20"
>
Expand Down Expand Up @@ -630,7 +630,7 @@ exports[`Alert Variant neutral Should render a neutral alert with dismiss button
<span
className="emotion-0"
color="colorTextNeutral"
data-paste-element="BOX"
data-paste-element="ICON"
display="block"
size="sizeIcon20"
>
Expand Down Expand Up @@ -694,7 +694,7 @@ exports[`Alert Variant neutral Should render a neutral alert with dismiss button
<span
className="emotion-3"
color="colorTextIcon"
data-paste-element="BOX"
data-paste-element="ICON"
display="block"
size="sizeIcon20"
>
Expand Down Expand Up @@ -804,7 +804,7 @@ exports[`Alert Variant warning Should render an warning alert 1`] = `
<span
className="emotion-0"
color="colorTextWarning"
data-paste-element="BOX"
data-paste-element="ICON"
display="block"
size="sizeIcon20"
>
Expand Down Expand Up @@ -995,7 +995,7 @@ exports[`Alert Variant warning Should render an warning alert with dismiss butto
<span
className="emotion-0"
color="colorTextWarning"
data-paste-element="BOX"
data-paste-element="ICON"
display="block"
size="sizeIcon20"
>
Expand Down Expand Up @@ -1061,7 +1061,7 @@ exports[`Alert Variant warning Should render an warning alert with dismiss butto
<span
className="emotion-3"
color="colorTextIcon"
data-paste-element="BOX"
data-paste-element="ICON"
display="block"
size="sizeIcon20"
>
Expand Down
49 changes: 49 additions & 0 deletions packages/paste-icons/__test__/icons.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import * as React from 'react';
import {matchers} from 'jest-emotion';
import {render} from '@testing-library/react';
import {CustomizationProvider} from '@twilio-paste/customization';
import {AgentIcon} from '../src/AgentIcon';

expect.extend(matchers);

describe('Icons', () => {
describe('HTML attributes', () => {
it('should have the default element name', () => {
const {container} = render(<AgentIcon decorative />);
expect(container.querySelector('[data-paste-element="ICON"]')).toBeInTheDocument();
});
it('should have a custom element name', () => {
const {container} = render(<AgentIcon element="CUSTOM_ICON" decorative />);
expect(container.querySelector('[data-paste-element="CUSTOM_ICON"]')).toBeInTheDocument();
});
});

describe('Customization', () => {
it('should apply custom styles to customizaed icons', () => {
const {container} = render(
<CustomizationProvider
baseTheme="default"
elements={{
ICON: {borderRadius: 'borderRadius20', backgroundColor: 'colorBackgroundSuccess'},
CUSTOM_ICON: {borderRadius: 'borderRadius20', backgroundColor: 'colorBackgroundDestructive'},
}}
>
<>
<AgentIcon decorative />
<AgentIcon element="CUSTOM_ICON" decorative />
</>
</CustomizationProvider>
);
expect(container.querySelector('[data-paste-element="ICON"]')).toHaveStyleRule('border-radius', '4px');
expect(container.querySelector('[data-paste-element="ICON"]')).toHaveStyleRule(
'background-color',
'rgb(20,176,83)'
);
expect(container.querySelector('[data-paste-element="CUSTOM_ICON"]')).toHaveStyleRule('border-radius', '4px');
expect(container.querySelector('[data-paste-element="CUSTOM_ICON"]')).toHaveStyleRule(
'background-color',
'rgb(214,31,31)'
);
});
});
});
4 changes: 2 additions & 2 deletions packages/paste-icons/src/AgentIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ export interface AgentIconProps extends IconWrapperProps {
decorative: boolean;
}

const AgentIcon: React.FC<AgentIconProps> = ({as, display, size, color, title, decorative}) => {
const AgentIcon: React.FC<AgentIconProps> = ({as, display, element = 'ICON', size, color, title, decorative}) => {
const titleId = `AgentIcon-${useUID()}`;

if (!decorative && title == null) {
throw new Error('[AgentIcon]: Missing a title for non-decorative icon.');
}

return (
<IconWrapper as={as} display={display} size={size} color={color}>
<IconWrapper as={as} display={display} element={element} size={size} color={color}>
<svg role="img" aria-hidden={decorative} width="100%" height="100%" viewBox="0 0 20 20" aria-labelledby={titleId}>
{title ? <title id={titleId}>{title}</title> : null}
<path
Expand Down
12 changes: 10 additions & 2 deletions packages/paste-icons/src/ArrowBackIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,23 @@ export interface ArrowBackIconProps extends IconWrapperProps {
decorative: boolean;
}

const ArrowBackIcon: React.FC<ArrowBackIconProps> = ({as, display, size, color, title, decorative}) => {
const ArrowBackIcon: React.FC<ArrowBackIconProps> = ({
as,
display,
element = 'ICON',
size,
color,
title,
decorative,
}) => {
const titleId = `ArrowBackIcon-${useUID()}`;

if (!decorative && title == null) {
throw new Error('[ArrowBackIcon]: Missing a title for non-decorative icon.');
}

return (
<IconWrapper as={as} display={display} size={size} color={color}>
<IconWrapper as={as} display={display} element={element} size={size} color={color}>
<svg role="img" aria-hidden={decorative} width="100%" height="100%" viewBox="0 0 20 20" aria-labelledby={titleId}>
{title ? <title id={titleId}>{title}</title> : null}
<path
Expand Down
12 changes: 10 additions & 2 deletions packages/paste-icons/src/ArrowDownIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,23 @@ export interface ArrowDownIconProps extends IconWrapperProps {
decorative: boolean;
}

const ArrowDownIcon: React.FC<ArrowDownIconProps> = ({as, display, size, color, title, decorative}) => {
const ArrowDownIcon: React.FC<ArrowDownIconProps> = ({
as,
display,
element = 'ICON',
size,
color,
title,
decorative,
}) => {
const titleId = `ArrowDownIcon-${useUID()}`;

if (!decorative && title == null) {
throw new Error('[ArrowDownIcon]: Missing a title for non-decorative icon.');
}

return (
<IconWrapper as={as} display={display} size={size} color={color}>
<IconWrapper as={as} display={display} element={element} size={size} color={color}>
<svg role="img" aria-hidden={decorative} width="100%" height="100%" viewBox="0 0 20 20" aria-labelledby={titleId}>
{title ? <title id={titleId}>{title}</title> : null}
<path
Expand Down
12 changes: 10 additions & 2 deletions packages/paste-icons/src/ArrowForwardIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,23 @@ export interface ArrowForwardIconProps extends IconWrapperProps {
decorative: boolean;
}

const ArrowForwardIcon: React.FC<ArrowForwardIconProps> = ({as, display, size, color, title, decorative}) => {
const ArrowForwardIcon: React.FC<ArrowForwardIconProps> = ({
as,
display,
element = 'ICON',
size,
color,
title,
decorative,
}) => {
const titleId = `ArrowForwardIcon-${useUID()}`;

if (!decorative && title == null) {
throw new Error('[ArrowForwardIcon]: Missing a title for non-decorative icon.');
}

return (
<IconWrapper as={as} display={display} size={size} color={color}>
<IconWrapper as={as} display={display} element={element} size={size} color={color}>
<svg role="img" aria-hidden={decorative} width="100%" height="100%" viewBox="0 0 20 20" aria-labelledby={titleId}>
{title ? <title id={titleId}>{title}</title> : null}
<path
Expand Down
4 changes: 2 additions & 2 deletions packages/paste-icons/src/ArrowUpIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ export interface ArrowUpIconProps extends IconWrapperProps {
decorative: boolean;
}

const ArrowUpIcon: React.FC<ArrowUpIconProps> = ({as, display, size, color, title, decorative}) => {
const ArrowUpIcon: React.FC<ArrowUpIconProps> = ({as, display, element = 'ICON', size, color, title, decorative}) => {
const titleId = `ArrowUpIcon-${useUID()}`;

if (!decorative && title == null) {
throw new Error('[ArrowUpIcon]: Missing a title for non-decorative icon.');
}

return (
<IconWrapper as={as} display={display} size={size} color={color}>
<IconWrapper as={as} display={display} element={element} size={size} color={color}>
<svg role="img" aria-hidden={decorative} width="100%" height="100%" viewBox="0 0 20 20" aria-labelledby={titleId}>
{title ? <title id={titleId}>{title}</title> : null}
<path
Expand Down
4 changes: 2 additions & 2 deletions packages/paste-icons/src/AttachIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ export interface AttachIconProps extends IconWrapperProps {
decorative: boolean;
}

const AttachIcon: React.FC<AttachIconProps> = ({as, display, size, color, title, decorative}) => {
const AttachIcon: React.FC<AttachIconProps> = ({as, display, element = 'ICON', size, color, title, decorative}) => {
const titleId = `AttachIcon-${useUID()}`;

if (!decorative && title == null) {
throw new Error('[AttachIcon]: Missing a title for non-decorative icon.');
}

return (
<IconWrapper as={as} display={display} size={size} color={color}>
<IconWrapper as={as} display={display} element={element} size={size} color={color}>
<svg role="img" aria-hidden={decorative} width="100%" height="100%" viewBox="0 0 20 20" aria-labelledby={titleId}>
{title ? <title id={titleId}>{title}</title> : null}
<path
Expand Down
12 changes: 10 additions & 2 deletions packages/paste-icons/src/AttachmentIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,23 @@ export interface AttachmentIconProps extends IconWrapperProps {
decorative: boolean;
}

const AttachmentIcon: React.FC<AttachmentIconProps> = ({as, display, size, color, title, decorative}) => {
const AttachmentIcon: React.FC<AttachmentIconProps> = ({
as,
display,
element = 'ICON',
size,
color,
title,
decorative,
}) => {
const titleId = `AttachmentIcon-${useUID()}`;

if (!decorative && title == null) {
throw new Error('[AttachmentIcon]: Missing a title for non-decorative icon.');
}

return (
<IconWrapper as={as} display={display} size={size} color={color}>
<IconWrapper as={as} display={display} element={element} size={size} color={color}>
<svg
role="img"
aria-hidden={decorative}
Expand Down
4 changes: 2 additions & 2 deletions packages/paste-icons/src/BoldIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ export interface BoldIconProps extends IconWrapperProps {
decorative: boolean;
}

const BoldIcon: React.FC<BoldIconProps> = ({as, display, size, color, title, decorative}) => {
const BoldIcon: React.FC<BoldIconProps> = ({as, display, element = 'ICON', size, color, title, decorative}) => {
const titleId = `BoldIcon-${useUID()}`;

if (!decorative && title == null) {
throw new Error('[BoldIcon]: Missing a title for non-decorative icon.');
}

return (
<IconWrapper as={as} display={display} size={size} color={color}>
<IconWrapper as={as} display={display} element={element} size={size} color={color}>
<svg
role="img"
aria-hidden={decorative}
Expand Down
4 changes: 2 additions & 2 deletions packages/paste-icons/src/BuiltInIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ export interface BuiltInIconProps extends IconWrapperProps {
decorative: boolean;
}

const BuiltInIcon: React.FC<BuiltInIconProps> = ({as, display, size, color, title, decorative}) => {
const BuiltInIcon: React.FC<BuiltInIconProps> = ({as, display, element = 'ICON', size, color, title, decorative}) => {
const titleId = `BuiltInIcon-${useUID()}`;

if (!decorative && title == null) {
throw new Error('[BuiltInIcon]: Missing a title for non-decorative icon.');
}

return (
<IconWrapper as={as} display={display} size={size} color={color}>
<IconWrapper as={as} display={display} element={element} size={size} color={color}>
<svg role="img" aria-hidden={decorative} width="100%" height="100%" viewBox="0 0 20 20" aria-labelledby={titleId}>
{title ? <title id={titleId}>{title}</title> : null}
<path
Expand Down
4 changes: 2 additions & 2 deletions packages/paste-icons/src/CalendarIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ export interface CalendarIconProps extends IconWrapperProps {
decorative: boolean;
}

const CalendarIcon: React.FC<CalendarIconProps> = ({as, display, size, color, title, decorative}) => {
const CalendarIcon: React.FC<CalendarIconProps> = ({as, display, element = 'ICON', size, color, title, decorative}) => {
const titleId = `CalendarIcon-${useUID()}`;

if (!decorative && title == null) {
throw new Error('[CalendarIcon]: Missing a title for non-decorative icon.');
}

return (
<IconWrapper as={as} display={display} size={size} color={color}>
<IconWrapper as={as} display={display} element={element} size={size} color={color}>
<svg role="img" aria-hidden={decorative} width="100%" height="100%" viewBox="0 0 20 20" aria-labelledby={titleId}>
{title ? <title id={titleId}>{title}</title> : null}
<path
Expand Down
4 changes: 2 additions & 2 deletions packages/paste-icons/src/ChatIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ export interface ChatIconProps extends IconWrapperProps {
decorative: boolean;
}

const ChatIcon: React.FC<ChatIconProps> = ({as, display, size, color, title, decorative}) => {
const ChatIcon: React.FC<ChatIconProps> = ({as, display, element = 'ICON', size, color, title, decorative}) => {
const titleId = `ChatIcon-${useUID()}`;

if (!decorative && title == null) {
throw new Error('[ChatIcon]: Missing a title for non-decorative icon.');
}

return (
<IconWrapper as={as} display={display} size={size} color={color}>
<IconWrapper as={as} display={display} element={element} size={size} color={color}>
<svg
role="img"
aria-hidden={decorative}
Expand Down
12 changes: 10 additions & 2 deletions packages/paste-icons/src/CheckboxCheckIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,23 @@ export interface CheckboxCheckIconProps extends IconWrapperProps {
decorative: boolean;
}

const CheckboxCheckIcon: React.FC<CheckboxCheckIconProps> = ({as, display, size, color, title, decorative}) => {
const CheckboxCheckIcon: React.FC<CheckboxCheckIconProps> = ({
as,
display,
element = 'ICON',
size,
color,
title,
decorative,
}) => {
const titleId = `CheckboxCheckIcon-${useUID()}`;

if (!decorative && title == null) {
throw new Error('[CheckboxCheckIcon]: Missing a title for non-decorative icon.');
}

return (
<IconWrapper as={as} display={display} size={size} color={color}>
<IconWrapper as={as} display={display} element={element} size={size} color={color}>
<svg role="img" aria-hidden={decorative} width="100%" height="100%" viewBox="0 0 20 20" aria-labelledby={titleId}>
{title ? <title id={titleId}>{title}</title> : null}
<path
Expand Down
Loading

0 comments on commit c59aa86

Please sign in to comment.