From 65fdf8e2aecc1711467cf200fe3b773566ad9488 Mon Sep 17 00:00:00 2001 From: Jasper Swart Date: Mon, 18 May 2020 13:39:00 +0200 Subject: [PATCH] Refactor svgFill utility to accept theme functions and CSS colors --- CHANGELOG.md | 3 +- examples/create-react-app/src/App.jsx | 2 +- packages/asc-ui/README.md | 4 +- .../asc-ui/src/components/Alert/AlertStyle.ts | 2 +- .../src/components/Button/ButtonStyle.ts | 18 +++--- .../Button/ShareButton/ShareButtonStyle.ts | 2 +- .../src/components/Checkbox/Checkbox.test.tsx | 4 +- .../src/components/Checkbox/CheckboxStyle.ts | 12 ++-- .../FooterLinkList/FooterLinkListItem.tsx | 4 +- .../FooterToggle/ToggleFooterHeader.tsx | 4 +- packages/asc-ui/src/components/Icon/Icon.tsx | 7 +-- .../asc-ui/src/components/Icon/IconStyle.ts | 15 ++--- .../asc-ui/src/components/Link/LinkStyle.ts | 24 +++---- .../Menu/MenuButton/MenuButtonStyle.ts | 2 +- .../TopTaskLink/TopTaskLinkStyle.ts | 2 +- packages/asc-ui/src/utils/README.md | 4 -- .../src/utils/__tests__/themeUtils.test.tsx | 26 ++++---- packages/asc-ui/src/utils/themeUtils.ts | 62 +++++++++++-------- 18 files changed, 98 insertions(+), 99 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4d24bea4..0a8de044e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,8 +16,9 @@ Prefix the change with one of these keywords: ## Unreleased -- Changed: **BREAKING** removed the `compact` prop/variant in `Alert` - Changed: updated the styling of `Alert` conform to design system +- Changed: **BREAKING** removed the `compact` prop/variant in `Alert` +- Changed: **BREAKING** `svgFill` now only accepts CSS color literal or a `ThemeFn` which returns a CSS color (see: https://github.com/Amsterdam/amsterdam-styled-components/pull/594). ## [0.20.0] diff --git a/examples/create-react-app/src/App.jsx b/examples/create-react-app/src/App.jsx index 98cbfec1a..b32751c02 100644 --- a/examples/create-react-app/src/App.jsx +++ b/examples/create-react-app/src/App.jsx @@ -50,7 +50,7 @@ const StyledLinkListItem = styled(ListItem)` } ${styles.IconStyle} { - ${svgFill('primary')}; + ${svgFill(themeColor('primary', 'main'))}; } ` diff --git a/packages/asc-ui/README.md b/packages/asc-ui/README.md index 10453807a..30a71e372 100644 --- a/packages/asc-ui/README.md +++ b/packages/asc-ui/README.md @@ -67,14 +67,14 @@ const StyledListItem - styled(ListItem)` color: ${themeColor('primary')}; ${styles.IconStyle} { - ${svgFill('tint', 'level1')}; + ${svgFill(themeColor('tint', 'level1'))}; } ${styles.LinkStyle} { &:hover { ${IconStyle} { - ${svgFill('primary')}; + ${svgFill(themeColor('primary', 'main'))}; } } } diff --git a/packages/asc-ui/src/components/Alert/AlertStyle.ts b/packages/asc-ui/src/components/Alert/AlertStyle.ts index 9b07f3c64..d11fdb943 100644 --- a/packages/asc-ui/src/components/Alert/AlertStyle.ts +++ b/packages/asc-ui/src/components/Alert/AlertStyle.ts @@ -77,7 +77,7 @@ export default styled.div` })}; ${(level === 'attention' || level === 'error') && css` - ${svgFill('tint', 'level1')({ theme })} + ${svgFill(themeColor('tint', 'level1'))} &, & * { color: ${themeColor('tint', 'level1')}; } diff --git a/packages/asc-ui/src/components/Button/ButtonStyle.ts b/packages/asc-ui/src/components/Button/ButtonStyle.ts index 24ce71cbb..802cabf12 100644 --- a/packages/asc-ui/src/components/Button/ButtonStyle.ts +++ b/packages/asc-ui/src/components/Button/ButtonStyle.ts @@ -69,7 +69,7 @@ const getVariant = () => ({ return css` background-color: ${themeColor('primary')}; color: ${readableColor(themeColor('primary')({ theme }))}; - ${svgFill('tint', 'level1')}; + ${svgFill(themeColor('tint', 'level1'))}; &:hover { background-color: ${darken(0.1, themeColor('primary')({ theme }))}; @@ -80,7 +80,7 @@ const getVariant = () => ({ return css` background-color: ${themeColor('secondary')}; color: ${themeColor('tint', 'level1')}; - ${svgFill('tint', 'level1')}; + ${svgFill(themeColor('tint', 'level1'))}; &:hover { background-color: ${darken(0.1, themeColor('secondary')({ theme }))}; @@ -101,7 +101,7 @@ const getVariant = () => ({ case 'tertiary': return css` background-color: ${themeColor('tint', 'level4')}; - ${svgFill('tint', 'level7')}; + ${svgFill(themeColor('tint', 'level7'))}; &:hover { background-color: ${darken( @@ -115,7 +115,7 @@ const getVariant = () => ({ return css` color: ${themeColor('primary')}; border: 1px solid ${themeColor('primary')}; - ${svgFill('primary')}; + ${svgFill(themeColor('primary', 'main'))}; &:hover { outline: 1px solid ${themeColor('primary')}; @@ -131,14 +131,14 @@ const getVariant = () => ({ text-align: left; color: ${themeColor('primary')}; background-color: rgba(0, 0, 0, 0); - ${svgFill('primary')}; + ${svgFill(themeColor('primary', 'main'))}; /* remove transition because it's async with Icon */ ${transitions('color', '0s')} &:hover { color: ${themeColor('secondary')}; - ${svgFill('secondary')}; + ${svgFill(themeColor('secondary', 'main'))}; } ${IconLeft} { @@ -152,7 +152,7 @@ const getVariant = () => ({ case 'blank': return css` background-color: ${themeColor('tint', 'level1')}; - ${svgFill('tint', 'level7')} + ${svgFill(themeColor('tint', 'level7'))}; &:hover { background-color: ${themeColor('tint', 'level3')}; } @@ -163,7 +163,7 @@ const getVariant = () => ({ background-color: ${themeColor('tint', 'level1')}; height: 32px; padding: ${themeSpacing(1, 2)}; - ${svgFill('tint', 'level7')} + ${svgFill(themeColor('tint', 'level7'))}; &:hover { background-color: ${themeColor('tint', 'level4')}; } @@ -264,7 +264,7 @@ const ButtonStyle = styled.button` border: none; color: ${themeColor('tint', 'level4')}; background-color: ${themeColor('tint', 'level3')}; - ${svgFill('tint', 'level4')}; + ${svgFill(themeColor('tint', 'level4'))}; text-decoration: none; ${({ taskflow }) => taskflow && diff --git a/packages/asc-ui/src/components/Button/ShareButton/ShareButtonStyle.ts b/packages/asc-ui/src/components/Button/ShareButton/ShareButtonStyle.ts index 1ed61b5e3..a4d3b37d1 100644 --- a/packages/asc-ui/src/components/Button/ShareButton/ShareButtonStyle.ts +++ b/packages/asc-ui/src/components/Button/ShareButton/ShareButtonStyle.ts @@ -19,7 +19,7 @@ const ShareButtonStyle = styled(ButtonStyle)` } ${IconStyle} { - ${svgFill('tint', 'level1')}; + ${svgFill(themeColor('tint', 'level1'))}; } ` diff --git a/packages/asc-ui/src/components/Checkbox/Checkbox.test.tsx b/packages/asc-ui/src/components/Checkbox/Checkbox.test.tsx index 1e47d3476..a1999d12f 100644 --- a/packages/asc-ui/src/components/Checkbox/Checkbox.test.tsx +++ b/packages/asc-ui/src/components/Checkbox/Checkbox.test.tsx @@ -12,7 +12,7 @@ describe('Checkbox', () => { cleanup() ;({ container, rerender } = render( - + , )) }) @@ -43,7 +43,7 @@ describe('Checkbox', () => { // Toggle on by changing props rerender( - + , ) diff --git a/packages/asc-ui/src/components/Checkbox/CheckboxStyle.ts b/packages/asc-ui/src/components/Checkbox/CheckboxStyle.ts index fec94e592..36afc7017 100644 --- a/packages/asc-ui/src/components/Checkbox/CheckboxStyle.ts +++ b/packages/asc-ui/src/components/Checkbox/CheckboxStyle.ts @@ -1,7 +1,7 @@ import styled, { css } from 'styled-components' -import { themeColor, focusStyleOutline, svgFill } from '../../utils' -import { IconStyle } from '../Icon' +import { focusStyleOutline, svgFill, themeColor } from '../../utils' import { outlineStyle } from '../../utils/themeUtils' +import { IconStyle } from '../Icon' type CheckboxVariant = 'primary' | 'secondary' | 'tertiary' @@ -19,28 +19,28 @@ const getVariant = () => ({ variant }: Props) => { return css` color: ${themeColor('primary', 'main')}; background-color: ${themeColor('primary', 'main')}; - ${svgFill('tint', 'level1')}; + ${svgFill(themeColor('tint', 'level1'))}; ` case 'secondary': return css` color: ${themeColor('secondary', 'main')}; background-color: ${themeColor('secondary', 'main')}; - ${svgFill('tint', 'level1')}; + ${svgFill(themeColor('tint', 'level1'))}; ` case 'tertiary': return css` color: ${themeColor('tint', 'level7')}; background-color: ${themeColor('tint', 'level1')}; - ${svgFill('tint', 'level7')}; + ${svgFill(themeColor('tint', 'level7'))}; ` default: return css` color: ${themeColor('tint', 'level7')}; background-color: ${themeColor('tint', 'level7')}; - ${svgFill('tint', 'level1')}; + ${svgFill(themeColor('tint', 'level1'))}; ` } } diff --git a/packages/asc-ui/src/components/Footer/FooterLinkList/FooterLinkListItem.tsx b/packages/asc-ui/src/components/Footer/FooterLinkList/FooterLinkListItem.tsx index 2acd1f2c0..2c2e61b7b 100644 --- a/packages/asc-ui/src/components/Footer/FooterLinkList/FooterLinkListItem.tsx +++ b/packages/asc-ui/src/components/Footer/FooterLinkList/FooterLinkListItem.tsx @@ -12,7 +12,7 @@ const StyledListItem = styled(ListItem)` } ${IconStyle} { - ${svgFill('tint', 'level1')}; + ${svgFill(themeColor('tint', 'level1'))}; } ${LinkStyle} { @@ -24,7 +24,7 @@ const StyledListItem = styled(ListItem)` text-decoration: underline; ${IconStyle} { - ${svgFill('tint', 'level1')}; + ${svgFill(themeColor('tint', 'level1'))}; } } } diff --git a/packages/asc-ui/src/components/Footer/FooterToggle/ToggleFooterHeader.tsx b/packages/asc-ui/src/components/Footer/FooterToggle/ToggleFooterHeader.tsx index db88466d9..50c8b46c1 100644 --- a/packages/asc-ui/src/components/Footer/FooterToggle/ToggleFooterHeader.tsx +++ b/packages/asc-ui/src/components/Footer/FooterToggle/ToggleFooterHeader.tsx @@ -5,7 +5,7 @@ import Icon from '../../Icon' import { ToggleHandlerProps } from '../../Toggle' import IconStyle from '../../Icon/IconStyle' import FooterHeading from '../FooterHeading' -import { svgFill } from '../../../utils' +import { svgFill, themeColor } from '../../../utils' export type Props = ToggleHandlerProps @@ -19,7 +19,7 @@ const ToggleFooterHeader: React.FC = ({ open, onClick, title }) => { & > ${IconStyle} { margin-right: 8px; - ${svgFill('tint', 'level1')}; + ${svgFill(themeColor('tint', 'level1'))}; } ` diff --git a/packages/asc-ui/src/components/Icon/Icon.tsx b/packages/asc-ui/src/components/Icon/Icon.tsx index 6c6617757..9f25a33e4 100644 --- a/packages/asc-ui/src/components/Icon/Icon.tsx +++ b/packages/asc-ui/src/components/Icon/Icon.tsx @@ -1,12 +1,7 @@ -import React from 'react' import IconStyle, { defaultProps, Props as IconStyleProps } from './IconStyle' export { defaultProps } export type Props = IconStyleProps -const Icon: React.FC = ({ children, ...otherProps }) => ( - {children} -) - -export default Icon +export default IconStyle diff --git a/packages/asc-ui/src/components/Icon/IconStyle.ts b/packages/asc-ui/src/components/Icon/IconStyle.ts index 55df78c34..233311932 100644 --- a/packages/asc-ui/src/components/Icon/IconStyle.ts +++ b/packages/asc-ui/src/components/Icon/IconStyle.ts @@ -1,8 +1,10 @@ import styled, { css } from 'styled-components' +import { svgFill } from '../../utils' +import { ThemeFn } from '../../utils/themeUtils' export type Props = { inline?: boolean - color?: string + color?: string | ThemeFn iconUrl?: string size?: number padding?: number @@ -41,16 +43,7 @@ const IconStyle = styled.span` `} } - ${({ color }) => css` - & svg { - circle, - rect, - polygon, - path { - fill: ${color}; - } - } - `}; + ${({ color }) => color && svgFill(color)}; ` export default IconStyle diff --git a/packages/asc-ui/src/components/Link/LinkStyle.ts b/packages/asc-ui/src/components/Link/LinkStyle.ts index 0ec49b416..dbb804dd8 100644 --- a/packages/asc-ui/src/components/Link/LinkStyle.ts +++ b/packages/asc-ui/src/components/Link/LinkStyle.ts @@ -1,13 +1,13 @@ import styled, { css } from 'styled-components' import { themeColor } from '../../utils' import { - svgFill, FocusStyle, getFocusStyle, + svgFill, themeSpacing, } from '../../utils/themeUtils' -import Typography, { TypographyProps } from '../Typography' import IconStyle from '../Icon/IconStyle' +import Typography, { TypographyProps } from '../Typography' export type LinkVariant = 'inline' | 'blank' | 'with-chevron' @@ -42,28 +42,28 @@ export const DefaultLinkStyleCSS = css` display: inline-flex; text-decoration: none; font-weight: 700; - color: ${({ color: colorOverride, theme }) => - themeColor('tint', 'level7', colorOverride)({ theme })}; + color: ${({ color: colorOverride }) => + themeColor('tint', 'level7', colorOverride)}; ${IconStyle} { margin: ${themeSpacing(1, 1, 0, 0)}; - ${({ color: colorOverride, theme, onDarkBackground }) => + ${({ color: colorOverride, onDarkBackground }) => onDarkBackground - ? svgFill('tint', 'level1')({ theme }) - : svgFill('tint', 'level7', colorOverride)({ theme })}; + ? svgFill(themeColor('tint', 'level1')) + : svgFill(themeColor('tint', 'level7', colorOverride))} } &:hover { text-decoration: underline; - ${({ color: colorOverride, theme, onDarkBackground }) => + ${({ color: colorOverride, onDarkBackground }) => css` color: ${onDarkBackground - ? themeColor('tint', 'level1')({ theme }) - : themeColor('secondary', 'main', colorOverride)({ theme })}; + ? themeColor('tint', 'level1') + : themeColor('supplement', 'main', colorOverride)}; ${IconStyle} { ${onDarkBackground - ? svgFill('tint', 'level1')({ theme }) - : svgFill('secondary', 'main', colorOverride)({ theme })} + ? svgFill(themeColor('tint', 'level1')) + : svgFill(themeColor('secondary', 'main', colorOverride))} } `} } diff --git a/packages/asc-ui/src/components/Menu/MenuButton/MenuButtonStyle.ts b/packages/asc-ui/src/components/Menu/MenuButton/MenuButtonStyle.ts index 6ef2c4976..ce1b1eb7c 100644 --- a/packages/asc-ui/src/components/Menu/MenuButton/MenuButtonStyle.ts +++ b/packages/asc-ui/src/components/Menu/MenuButton/MenuButtonStyle.ts @@ -47,7 +47,7 @@ export const MenuButtonBaseStyle = styled(Button)` const activeStyle = css` color: ${themeColor('secondary')}; - ${svgFill('secondary')} + ${svgFill(themeColor('secondary', 'main'))} ${MenuButtonTextStyle} { color: ${themeColor('secondary')}; diff --git a/packages/asc-ui/src/components/TopTaskLink/TopTaskLinkStyle.ts b/packages/asc-ui/src/components/TopTaskLink/TopTaskLinkStyle.ts index f67080968..ee89c3fd1 100644 --- a/packages/asc-ui/src/components/TopTaskLink/TopTaskLinkStyle.ts +++ b/packages/asc-ui/src/components/TopTaskLink/TopTaskLinkStyle.ts @@ -45,6 +45,6 @@ export default styled.a<{ ${IconStyle} { margin-bottom: ${themeSpacing(2)}; - ${svgFill('tint', 'level7')}; + ${svgFill(themeColor('tint', 'level7'))}; } ` diff --git a/packages/asc-ui/src/utils/README.md b/packages/asc-ui/src/utils/README.md index a77e7d67f..c64ad7fa9 100644 --- a/packages/asc-ui/src/utils/README.md +++ b/packages/asc-ui/src/utils/README.md @@ -86,7 +86,3 @@ const HeaderSearchStyle = styled.div` ` return ``` - -## svgFill - -... diff --git a/packages/asc-ui/src/utils/__tests__/themeUtils.test.tsx b/packages/asc-ui/src/utils/__tests__/themeUtils.test.tsx index 2ff663a30..5b7becc31 100644 --- a/packages/asc-ui/src/utils/__tests__/themeUtils.test.tsx +++ b/packages/asc-ui/src/utils/__tests__/themeUtils.test.tsx @@ -1,20 +1,20 @@ +import { render } from '@testing-library/react' import React from 'react' import styled from 'styled-components' -import { render } from '@testing-library/react' +import { BACKDROP_Z_INDEX } from '../../components/shared/constants' +import { ascDefaultTheme } from '../../theme' +import ThemeProvider from '../../theme/ThemeProvider' import { - themeColor, - focusStyleOutline, breakpoint, - svgFill, + focusStyleOutline, getTypographyFromTheme, getTypographyValueFromProperty, - themeSpacing, getValueFromTheme, showAboveBackDrop, + svgFill, + themeColor, + themeSpacing, } from '../themeUtils' -import { BACKDROP_Z_INDEX } from '../../components/shared/constants' -import { ascDefaultTheme } from '../../theme' -import ThemeProvider from '../../theme/ThemeProvider' const { colors, typography } = ascDefaultTheme @@ -121,12 +121,14 @@ describe('svgFill', () => { ...ascDefaultTheme, } - it("should return een empty string when the color doesn't exist", () => { - expect(svgFill()({ theme })).toBe('') + it('should return the right fill from a literal color', () => { + expect(svgFill('#004699')({ theme })).toContain('#004699') }) - it('should return the right fill color for the svg', () => { - expect(svgFill('tint', 'level5')({ theme })[1]).toContain('#767676') + it('should return the right fill from a theme color', () => { + expect(svgFill(themeColor('primary', 'main'))({ theme })).toContain( + '#004699', + ) }) }) diff --git a/packages/asc-ui/src/utils/themeUtils.ts b/packages/asc-ui/src/utils/themeUtils.ts index 00d789f23..11b557538 100644 --- a/packages/asc-ui/src/utils/themeUtils.ts +++ b/packages/asc-ui/src/utils/themeUtils.ts @@ -1,8 +1,7 @@ import { css, keyframes } from 'styled-components' +import { BACKDROP_Z_INDEX } from '../components/shared/constants' import { Theme } from '../types' - import { fromProps } from './fromProps' -import { BACKDROP_Z_INDEX } from '../components/shared/constants' import BreakpointsInterface = Theme.BreakpointsInterface import ThemeInterface = Theme.ThemeInterface @@ -13,6 +12,8 @@ type ThemeProp = { theme: Theme.ThemeInterface } +export type ThemeFn = ({ theme }: { theme: Theme.ThemeInterface }) => T + /** * Curry function to provide the theme as first parameter, followed up with other parameters * @param cb @@ -27,7 +28,7 @@ type ThemeProp = { * ` */ export const withTheme = ( - cb: (theme: ThemeInterface, ...params: T) => any, + cb: (theme: ThemeInterface, ...params: T) => U, ) => (...params: T) => ({ theme }: { theme: ThemeInterface }): U => cb(theme, ...params) @@ -160,7 +161,7 @@ export const outlineStyle = ( outline-width: ${width}px; ` -/* we have chosen here to use a dubble selector '&&'. +/* we have chosen here to use a dubble selector '&&'. This will override a hover state with outlines. introduced this when resolving issue: #131 */ @@ -218,27 +219,38 @@ export const srOnlyStyle = () => ({ srOnly }: { srOnly?: boolean }) => ` : '' -export const svgFill = withTheme<[Theme.ColorType?, string?, string?]>( - (theme, colorType, variant = 'main', override) => { - if (colorType) { - const value = themeColor(colorType, variant, override)({ theme }) - if (typeof value === 'string') { - return css` - & svg { - circle, - rect, - polygon, - path { - fill: ${value}; - } - } - ` +/** + * Fills the elements in an SVG with a color, useful for styling icons. + * + * For example, using a theme color: + * ```jsx + * const PrimaryCarIcon = styled(CarIcon)` + * ${svgFill(themeColor('primary'))} + * ` + * ``` + * + * Or by setting the color directly using any color valid in CSS, such as hexadecimal, rgba() and hsla(). + * + * ```jsx + * const RedCarIcon = styled(CarIcon)` + * ${svgFill('#FF0000')} + * ` + * ``` + */ +export const svgFill = withTheme((theme, color: string | ThemeFn) => { + const fill = typeof color === 'function' ? color({ theme }) : color + + return css` + & svg { + circle, + rect, + polygon, + path { + fill: ${fill}; } } - - return '' - }, -) + ` +}) /** * Adds an animated background to the element to indicate the content is loading. @@ -250,11 +262,11 @@ export const perceivedLoading = withTheme( 0% { background-color: ${themeColor('tint', 'level3')({ theme })}; } - + 50% { background-color: ${themeColor('tint', 'level4')({ theme })}; } - + 100% { background-color: ${themeColor('tint', 'level3')({ theme })}; }