Skip to content

feat(color): add color variant to the base color prop #1244

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions packages/lumx-react/src/components/icon/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import React, { forwardRef } from 'react';

import classNames from 'classnames';

import { ColorPalette, ColorVariant, Size, Theme } from '@lumx/react';
import { ColorPalette, ColorVariant, ColorWithVariants, Size, Theme } from '@lumx/react';
import { Comp, GenericProps, HasTheme } from '@lumx/react/utils/type';
import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
import { getRootClassName, handleBasicClasses, parseColorWithVariants } from '@lumx/react/utils/className';
import { mdiAlertCircle } from '@lumx/icons';

export type IconSizes = Extract<Size, 'xxs' | 'xs' | 's' | 'm' | 'l' | 'xl' | 'xxl'>;
Expand All @@ -14,7 +14,7 @@ export type IconSizes = Extract<Size, 'xxs' | 'xs' | 's' | 'm' | 'l' | 'xl' | 'x
*/
export interface IconProps extends GenericProps, HasTheme {
/** Color variant. */
color?: ColorPalette;
color?: ColorWithVariants;
/** Lightened or darkened variant of the selected icon color. */
colorVariant?: ColorVariant;
/** Whether the icon has a shape. */
Expand Down Expand Up @@ -53,7 +53,18 @@ const DEFAULT_PROPS: Partial<IconProps> = {};
* @return React element.
*/
export const Icon: Comp<IconProps, HTMLElement> = forwardRef((props, ref) => {
const { className, color, colorVariant, hasShape, icon, size, theme, alt, ...forwardedProps } = props;
const {
className,
color: propColor,
colorVariant: propColorVariant,
hasShape,
icon,
size,
theme,
alt,
...forwardedProps
} = props;
const [color, colorVariant] = parseColorWithVariants(propColor, propColorVariant);

// Color
let iconColor = color;
Expand Down
9 changes: 9 additions & 0 deletions packages/lumx-react/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ export const ColorVariant = {
} as const;
export type ColorVariant = ValueOf<typeof ColorVariant>;

/** ColorPalette with all possible color variant combination */
export type ColorWithVariants =
| ColorPalette
| Exclude<
`${ColorPalette}-${ColorVariant}`,
// No dark variant for light and dark
`light-D${number}` | `dark-D${number}`
>;

export const Theme = {
light: 'light',
dark: 'dark',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { Children, forwardRef, isValidElement } from 'react';

import classNames from 'classnames';

import { ColorPalette, ColorVariant, Typography } from '@lumx/react';
import { ColorVariant, ColorWithVariants, Typography } from '@lumx/react';
import { Comp, GenericProps } from '@lumx/react/utils/type';
import { getFontColorClassName, getRootClassName, getTypographyClassName } from '@lumx/react/utils/className';

Expand All @@ -13,7 +13,7 @@ export interface InlineListProps extends GenericProps {
/**
* Text color.
*/
color?: ColorPalette;
color?: ColorWithVariants;
/**
* Lightened or darkened variant of the selected color.
*/
Expand Down Expand Up @@ -52,7 +52,7 @@ const DEFAULT_PROPS = {} as const;
*/
export const InlineList: Comp<InlineListProps> = forwardRef((props, ref) => {
const { className, color, colorVariant, typography, children, wrap, ...forwardedProps } = props;
const fontColorClassName = color && getFontColorClassName(color, colorVariant);
const fontColorClassName = getFontColorClassName(color, colorVariant);
const typographyClassName = typography && getTypographyClassName(typography);
return (
// eslint-disable-next-line jsx-a11y/no-redundant-roles
Expand Down
11 changes: 6 additions & 5 deletions packages/lumx-react/src/components/link/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import isEmpty from 'lodash/isEmpty';

import classNames from 'classnames';

import { ColorPalette, ColorVariant, Icon, Size, Typography } from '@lumx/react';
import { ColorVariant, ColorWithVariants, Icon, Size, Typography } from '@lumx/react';
import { Comp, GenericProps } from '@lumx/react/utils/type';
import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
import { getRootClassName, handleBasicClasses, parseColorWithVariants } from '@lumx/react/utils/className';
import { renderLink } from '@lumx/react/utils/renderLink';

type HTMLAnchorProps = React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>;
Expand All @@ -16,7 +16,7 @@ type HTMLAnchorProps = React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAn
*/
export interface LinkProps extends GenericProps {
/** Color variant. */
color?: ColorPalette;
color?: ColorWithVariants;
/** Lightened or darkened variant of the selected icon color. */
colorVariant?: ColorVariant;
/** Link href. */
Expand Down Expand Up @@ -86,8 +86,8 @@ export const Link: Comp<LinkProps, HTMLAnchorElement | HTMLButtonElement> = forw
const {
children,
className,
color,
colorVariant,
color: propColor,
colorVariant: propColorVariant,
disabled,
isDisabled = disabled,
href,
Expand All @@ -98,6 +98,7 @@ export const Link: Comp<LinkProps, HTMLAnchorElement | HTMLButtonElement> = forw
typography,
...forwardedProps
} = props;
const [color, colorVariant] = parseColorWithVariants(propColor, propColorVariant);
const renderedChildren = useMemo(
() => (
<>
Expand Down
6 changes: 3 additions & 3 deletions packages/lumx-react/src/components/text/Text.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { Children, Fragment, forwardRef } from 'react';

import { Icon, ColorPalette, ColorVariant, Typography, WhiteSpace } from '@lumx/react';
import { Icon, ColorVariant, Typography, WhiteSpace, ColorWithVariants } from '@lumx/react';
import { Comp, GenericProps, TextElement, isComponent } from '@lumx/react/utils/type';
import {
getFontColorClassName,
Expand All @@ -19,7 +19,7 @@ export interface TextProps extends GenericProps {
/**
* Color variant.
*/
color?: ColorPalette;
color?: ColorWithVariants;
/**
* Lightened or darkened variant of the selected color.
*/
Expand Down Expand Up @@ -88,7 +88,7 @@ export const Text: Comp<TextProps> = forwardRef((props, ref) => {
...forwardedProps
} = props;

const colorClass = color && getFontColorClassName(color, colorVariant);
const colorClass = getFontColorClassName(color, colorVariant);
const typographyClass = typography && getTypographyClassName(typography);

// Truncate mode
Expand Down
20 changes: 17 additions & 3 deletions packages/lumx-react/src/utils/className.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CSS_PREFIX } from '@lumx/react/constants';

import kebabCase from 'lodash/kebabCase';
import { ColorPalette, ColorVariant, Typography } from '@lumx/react/components';
import { ColorPalette, ColorVariant, ColorWithVariants, Typography } from '@lumx/react/components';

// See https://regex101.com/r/YjS1uI/3
const LAST_PART_CLASSNAME = /^(.*)-(.+)$/gi;
Expand All @@ -27,12 +27,26 @@ export function getRootClassName(componentName: string, subComponent?: boolean):
return formattedClassName;
}

export function parseColorWithVariants(
colorWithVariants: ColorWithVariants | undefined,
colorVariant: ColorVariant = ColorVariant.N,
): [color?: ColorPalette, colorVariant?: ColorVariant] {
if (!colorWithVariants) return [undefined, colorVariant];
const [color, baseColorVariant] = colorWithVariants.split('-');
return [color as ColorPalette, (baseColorVariant || colorVariant) as ColorVariant];
}

/**
* Returns the classname associated to the given color and variant.
* For example, for 'dark' and 'L2' it returns `lumx-color-font-dark-l2`
*/
export const getFontColorClassName = (color: ColorPalette, colorVariant: ColorVariant = ColorVariant.N) => {
return `lumx-color-font-${color}-${colorVariant}`;
export const getFontColorClassName = (
colorWithVariants?: ColorWithVariants,
colorVariant: ColorVariant = ColorVariant.N,
): string | undefined => {
const [color, baseColorVariant] = parseColorWithVariants(colorWithVariants, colorVariant);
if (!color) return undefined;
return `lumx-color-font-${color}-${baseColorVariant || colorVariant}`;
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export const App = () => {
figure={<Thumbnail alt="" image="/demo-assets/square1.jpg" size="m" variant="rounded" />}
>
<Heading typography="body1">With thumbnail</Heading>
<Text as="p" typography="caption" color="dark" colorVariant="L2">
<Text as="p" typography="caption" color="dark-L2">
And secondary text
</Text>
</GenericBlock>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,10 @@ Containing one or more content elements ([Heading](/product/components/heading/)
<Heading as="h2" typography="subtitle2">
Marketing & Comms Workspace
</Heading>
<Text as="p" truncate typography="body1" color="dark" colorVariant="L2">
<Text as="p" truncate typography="body1" color="dark-L2">
A space to collaborate on events, communication and a workspace.
</Text>
<InlineList typography="body1" color="dark" colorVariant="L2">
<InlineList typography="body1" color="dark-L2">
<Text as="span">
<Icon icon={mdiEarth} /> Open
</Text>
Expand All @@ -117,7 +117,7 @@ Containing one or more content elements ([Heading](/product/components/heading/)
<Heading as="h2" typography="subtitle1">
Ines Gomez
</Heading>
<InlineList typography="body1" color="dark" colorVariant="L2">
<InlineList typography="body1" color="dark-L2">
<Text as="span">Account Manager</Text>
<Text as="span">
Added in <Link href="#">Sales Team</Link>
Expand All @@ -140,7 +140,7 @@ Containing one or more content elements ([Heading](/product/components/heading/)
<Heading as="h2" typography="subtitle2">
Fiscal Year Results and Conference
</Heading>
<Text as="p" typography="body1" color="dark" colorVariant="L2">
<Text as="p" typography="body1" color="dark-L2">
Our company will release its Fiscal Year Results next…
</Text>
</GenericBlock.Content>
Expand All @@ -153,7 +153,7 @@ Containing one or more content elements ([Heading](/product/components/heading/)
<Heading as="h2" typography="subtitle2">
Slack
</Heading>
<Text as="p" typography="body1" color="dark" colorVariant="L2">
<Text as="p" typography="body1" color="dark-L2">
A communication platform including chat rooms…
</Text>
</GenericBlock.Content>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { mdiEarth } from '@lumx/icons';
Like [text](/product/components/text/) elements, inline lists can have customized **color** and **typography** (see list of [typography](/product/foundations/typography/) and [color](/product/foundations/color/) styles).

<DemoBlock>
<InlineList typography="body1" color="dark" colorVariant="L2">
<InlineList typography="body1" color="dark-L2">
<Text as="span">Some text</Text>
<Link href="#">Some link</Link>
<Icon icon={mdiEarth} />
Expand All @@ -30,7 +30,7 @@ Like [text](/product/components/text/) elements, inline lists can have customize
Inline lists pair well with [text](/product/components/text/) `noWrap` property to disable line wrap and `truncate` property to cut text with ellipsis.

<DemoBlock orientation="horizontal" vAlign="space-around">
<InlineList typography="body1" color="dark" colorVariant="L2" style={{ maxWidth: 275 }}>
<InlineList typography="body1" color="dark-L2" style={{ maxWidth: 275 }}>
<Text as="span" noWrap>
<Icon icon={mdiEarth} />
Some text
Expand All @@ -44,7 +44,7 @@ Inline lists pair well with [text](/product/components/text/) `noWrap` property
Inversely, the use of the `wrap` property will activate line wrap on overflow.

<DemoBlock orientation="horizontal" vAlign="space-around">
<InlineList typography="body1" color="dark" colorVariant="L2" wrap style={{ maxWidth: 275 }}>
<InlineList typography="body1" color="dark-L2" wrap style={{ maxWidth: 275 }}>
<Text as="span">
<Icon icon={mdiEarth} />
Some text
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const App = ({ theme }: any) => {
<Tab
label="Tab 2"
icon={mdiBreadSliceOutline}
iconProps={{ color: 'red', colorVariant: 'L1', hasShape: false }}
iconProps={{ color: 'red-L1', hasShape: false }}
/>
<Tab label="Tab 3" icon={mdiSilverwareForkKnife} />
</TabList>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Both **color** and **typography** can be adapted on text elements (see list of [
<Text as="h2" typography="title2">
Title
</Text>
<Text as="p" typography="body1" color="dark" colorVariant="L2">
<Text as="p" typography="body1" color="dark-L2">
Some <Text as="span" color="red"><Icon icon={mdiHeart} />colored</Text> text
</Text>
</DemoBlock>
Expand Down