From c8ef70df57f2b0da813431f34e6d30be9e65aec1 Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Wed, 8 Jul 2020 16:41:34 -0700 Subject: [PATCH 01/22] feat: first pass at List component family --- .../List/Documentation/List.stories.tsx | 638 ++++++++++++++++++ .../List/Documentation/ListEmpty.md | 25 + src/Components/List/List.scss | 280 ++++++++ src/Components/List/List.tsx | 125 ++++ src/Components/List/ListDivider.tsx | 50 ++ src/Components/List/ListEmpty.tsx | 54 ++ src/Components/List/ListIcon.scss | 13 + src/Components/List/ListIcon.tsx | 21 + src/Components/List/ListIndicator.scss | 230 +++++++ src/Components/List/ListIndicator.tsx | 36 + src/Components/List/ListItem.tsx | 106 +++ src/Components/List/ListLinkItem.tsx | 62 ++ src/Components/List/index.tsx | 35 + src/index.ts | 1 + 14 files changed, 1676 insertions(+) create mode 100644 src/Components/List/Documentation/List.stories.tsx create mode 100644 src/Components/List/Documentation/ListEmpty.md create mode 100644 src/Components/List/List.scss create mode 100644 src/Components/List/List.tsx create mode 100644 src/Components/List/ListDivider.tsx create mode 100644 src/Components/List/ListEmpty.tsx create mode 100644 src/Components/List/ListIcon.scss create mode 100644 src/Components/List/ListIcon.tsx create mode 100644 src/Components/List/ListIndicator.scss create mode 100644 src/Components/List/ListIndicator.tsx create mode 100644 src/Components/List/ListItem.tsx create mode 100644 src/Components/List/ListLinkItem.tsx create mode 100644 src/Components/List/index.tsx diff --git a/src/Components/List/Documentation/List.stories.tsx b/src/Components/List/Documentation/List.stories.tsx new file mode 100644 index 00000000..658e9887 --- /dev/null +++ b/src/Components/List/Documentation/List.stories.tsx @@ -0,0 +1,638 @@ +// Libraries +import React, {createRef, RefObject, useRef} from 'react' +import marked from 'marked' + +// Storybook +import {storiesOf} from '@storybook/react' +import {withKnobs, text, select, boolean, object} from '@storybook/addon-knobs' +import {mapEnumKeys} from '../../../Utils/storybook' +import {useState} from '@storybook/addons' + +// Components +import { + List, + ListRef, + ListDividerRef, + ListItemRef, + ListEmptyRef, + ListLinkItemRef, +} from '../' +import {Popover} from '../../Popover' + +// Types +import { + ComponentColor, + ComponentSize, + IconFont, + Appearance, +} from '../../../Types' + +// Notes +import ListReadme from './List.md' +import ListDividerReadme from './ListDivider.md' +import ListItemReadme from './ListItem.md' +import ListEmptyReadme from './ListEmpty.md' +import ListLinkItemReadme from './ListLinkItem.md' + +const listFamilyStories = storiesOf( + 'Components|List/Family', + module +).addDecorator(withKnobs) + +const listExamplesStories = storiesOf( + 'Components|List/Examples', + module +).addDecorator(withKnobs) + +const defaultListStyle = {width: '250px'} + +interface ExampleDropdownItem { + type: 'item' | 'divider' + wrap: boolean + text: string +} + +const exampleItems: ExampleDropdownItem[] = [ + { + type: 'divider', + wrap: false, + text: 'Domestic Fruits', + }, + { + type: 'item', + wrap: false, + text: 'Banana', + }, + { + type: 'item', + wrap: false, + text: 'Kiwi', + }, + { + type: 'item', + wrap: false, + text: 'Lemon', + }, + { + type: 'item', + wrap: false, + text: 'Apple', + }, + { + type: 'item', + wrap: false, + text: 'Orange', + }, + { + type: 'item', + wrap: false, + text: 'Grapefruit', + }, + { + type: 'item', + wrap: false, + text: 'Pear', + }, + { + type: 'divider', + wrap: false, + text: 'Imported Fruits', + }, + { + type: 'item', + wrap: false, + text: 'Dragonfruit', + }, + { + type: 'item', + wrap: false, + text: 'Yuzu', + }, + { + type: 'item', + wrap: false, + text: 'Mango', + }, + { + type: 'item', + wrap: false, + text: 'Lychee', + }, + { + type: 'item', + wrap: false, + text: 'Passionfruit', + }, + { + type: 'item', + wrap: false, + text: 'Guava', + }, + { + type: 'divider', + wrap: false, + text: '', + }, + { + type: 'item', + wrap: true, + text: 'This dropdown item text is much longer to test text wrapping', + }, + { + type: 'item', + wrap: false, + text: + 'Long text that will be truncated if it exceeds the width of the List', + }, +] + +listFamilyStories.add( + 'List', + () => { + const [selectedItem, setSelectedItem] = useState('Grapefruit') + const listRef: RefObject = createRef() + + const logRef = (): void => { + /* eslint-disable */ + console.log(listRef.current) + /* eslint-enable */ + } + + const handleItemClick = (item: string): void => { + setSelectedItem(item) + } + + return ( +
+ + {exampleItems.map(item => { + if (item.type === 'item') { + return ( + + {item.text} + + ) + } + + return ( + + ) + })} + +
+ +
+
+ ) + }, + { + readme: { + content: marked(ListReadme), + }, + } +) + +listFamilyStories.add( + 'ListDivider', + () => { + const dropdownDividerRef: RefObject = createRef() + + const logRef = (): void => { + /* eslint-disable */ + console.log(dropdownDividerRef.current) + /* eslint-enable */ + } + + return ( +
+ +
+ +
+
+ ) + }, + { + readme: { + content: marked(ListDividerReadme), + }, + } +) + +listFamilyStories.add( + 'ListItem', + () => { + const listItemRef: RefObject = createRef() + + const logRef = (): void => { + /* eslint-disable */ + console.log(listItemRef.current) + /* eslint-enable */ + } + + return ( +
+ { + alert(`onClick returned: ${value}`) + }} + color={ + ComponentColor[ + select('color', mapEnumKeys(ComponentColor), 'Primary') + ] + } + size={ + ComponentSize[select('size', mapEnumKeys(ComponentSize), 'Small')] + } + disabled={boolean('disabled', false)} + > + {text('children (text)', 'I am a dropdown item!')} + +
+ +
+
+ ) + }, + { + readme: { + content: marked(ListItemReadme), + }, + } +) + +listFamilyStories.add( + 'ListEmpty', + () => { + const listEmptyRef: RefObject = createRef() + + const logRef = (): void => { + /* eslint-disable */ + console.log(listEmptyRef.current) + /* eslint-enable */ + } + + return ( +
+ + {text('children (text)', 'No items to display')} + +
+ +
+
+ ) + }, + { + readme: { + content: marked(ListEmptyReadme), + }, + } +) + +listFamilyStories.add( + 'ListLinkItem', + () => { + const dropdownLinkItemRef: RefObject = createRef() + + const logRef = (): void => { + /* eslint-disable */ + console.log(dropdownLinkItemRef.current) + /* eslint-enable */ + } + + return ( + + ) + }, + { + readme: { + content: marked(ListLinkItemReadme), + }, + } +) + +listExamplesStories.add( + 'Icons & Indicators', + () => { + const listItemRef: RefObject = createRef() + + const logRef = (): void => { + /* eslint-disable */ + console.log(listItemRef.current) + /* eslint-enable */ + } + + return ( +
+ { + alert(`onClick returned: ${value}`) + }} + color={ + ComponentColor[ + select('color', mapEnumKeys(ComponentColor), 'Primary') + ] + } + size={ + ComponentSize[select('size', mapEnumKeys(ComponentSize), 'Small')] + } + disabled={boolean('disabled', false)} + > + + {text('children (text)', 'I am a dropdown item!')} + + + { + alert(`onClick returned: ${value}`) + }} + color={ + ComponentColor[ + select('color', mapEnumKeys(ComponentColor), 'Primary') + ] + } + size={ + ComponentSize[select('size', mapEnumKeys(ComponentSize), 'Small')] + } + disabled={boolean('disabled', false)} + > + + {text('children (text)', 'I am a dropdown item!')} + + +
+ +
+
+ ) + }, + { + readme: { + content: marked(ListItemReadme), + }, + } +) + +type onHide = () => void | undefined + +listExamplesStories.add( + 'Used with a Popover', + () => { + const [turtleHat, setTurtleHat] = useState('Detective Hat') + const triggerRef = useRef(null) + + const handleToggleHat = (onHide: onHide) => (hat: string) => { + onHide && onHide() + setTurtleHat(hat) + } + + const noop = (): void => { + return + } + + return ( +
+
+ Pet Turtle +
+ ( + + + + + {'Put Turtle in Water'} + + + + {'Feed Turtle'} + + + + {'Put Turtle Outside'} + + + + {'Teach Turtle a Trick'} + + + + + {'Detective Hat'} + + + + {'Party Hat'} + + + + {'10 Gallon Hat'} + + + + {'Half Eaten Grape'} + + + )} + /> +
+ ) + }, + { + readme: { + content: marked(ListItemReadme), + }, + } +) diff --git a/src/Components/List/Documentation/ListEmpty.md b/src/Components/List/Documentation/ListEmpty.md new file mode 100644 index 00000000..8cae94e6 --- /dev/null +++ b/src/Components/List/Documentation/ListEmpty.md @@ -0,0 +1,25 @@ +# ListEmpty + +`ListEmpty` is part of the `List` component family and can be accessed from the single import. Use this component to indicate that a list does not have any items. + +### Usage + +```tsx +import {List, ComponentSize} from '@influxdata/clockface' +``` + +```tsx + + This list has no items + +``` + +### Example + + + + + + + + diff --git a/src/Components/List/List.scss b/src/Components/List/List.scss new file mode 100644 index 00000000..e41446df --- /dev/null +++ b/src/Components/List/List.scss @@ -0,0 +1,280 @@ +@import '../../Styles/variables'; + +$list-item--divider-color: rgba($g20-white, 0.03); +$list-item--divider-text: rgba($g20-white, 0.4); + +.cf-list { + overflow: hidden; + border-radius: $cf-radius; +} + +.cf-list--contents { + font-size: 0; + user-select: none; + display: flex; + flex-direction: column; + align-items: stretch; + cursor: pointer; +} + +.cf-list-item, +.cf-list-divider, +.cf-list-divider__thin { + margin-bottom: $cf-border; + + &:last-child { + margin-bottom: 0; + } +} + +/* + List Item + ------------------------------------------------------------------------------ +*/ + +@mixin listItemSharedStyles() { + user-select: none; + position: relative; + border-radius: $cf-radius-sm; + display: flex; + align-items: center; +} + +.cf-list-item { + @include listItemSharedStyles(); + transition: color 0.25s ease, background-color 0.25s ease; + + &:hover, + &.cf-list-item__active { + cursor: pointer; + } +} + +/* + List Item Text + ------------------------------------------------------------------------------ +*/ + +.cf-list-item--text { + flex: 1; +} + +.cf-list-item--text__wrap { + word-break: break-all; + white-space: pre-wrap; +} + +.cf-list-item--text__no-wrap { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* + Divider + ------------------------------------------------------------------------------ +*/ + +.cf-list-divider { + border-radius: $cf-radius-sm; + border-bottom: $cf-border solid $list-item--divider-color; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.cf-list-divider, +.cf-list-divider__thin { + color: $list-item--divider-text; + margin-bottom: $cf-border; + + &:hover { + cursor: default; + } +} + +.cf-list-divider__thin { + background-color: $list-item--divider-color; + padding: 0; + height: $cf-border; +} + +/* + Link Item + ------------------------------------------------------------------------------ +*/ + +.cf-list-link-item { + @include listItemSharedStyles(); + + > a { + transition: color 0.25s ease, background-color 0.25s ease; + display: inline-block; + border-radius: $cf-radius-sm; + } +} + +/* + Disabled State Item + ------------------------------------------------------------------------------ +*/ + +.cf-list-item__disabled, +.cf-list-item__disabled:hover, +.cf-list-link-item.cf-list-item__disabled a, +.cf-list-link-item.cf-list-item__disabled a:link, +.cf-list-link-item.cf-list-item__disabled a:visited, +.cf-list-link-item.cf-list-item__disabled a:active, +.cf-list-link-item.cf-list-item__disabled a:hover { + color: rgba($g20-white, 0.45); + font-style: italic; + cursor: default; +} + +/* + List Empty + ------------------------------------------------------------------------------ +*/ + +.cf-list-empty { + color: $g20-white; + text-align: center; + font-style: italic; + opacity: 0.38; + user-select: none; +} + +/* + Color Modifiers + ------------------------------------------------------------------------------ +*/ + +@mixin listItemColorModifier($color, $colorHover) { + &.cf-list-item { + color: $color; + + &:hover { + color: $colorHover; + background-color: rgba($g20-white, 0.05); + } + } + + &.cf-list-item.cf-list-item__active, + &.cf-list-item.cf-list-item__active:hover { + color: $g20-white; + background-color: rgba($g20-white, 0.05); + } + + &.cf-list-link-item > a:link, + &.cf-list-link-item > a:visited, + &.cf-list-link-item > a:active { + color: $color; + } + + &.cf-list-link-item > a:hover { + color: $colorHover; + background-color: rgba($g20-white, 0.05); + } + + &.cf-list-link-item__active > a:link, + &.cf-list-link-item__active > a:visited, + &.cf-list-link-item__active > a:active, + &.cf-list-link-item__active > a:hover { + color: $g20-white; + background-color: rgba($g20-white, 0.05); + } +} + +.cf-list__default { + @include listItemColorModifier($g13-mist, $g15-platinum); +} + +.cf-list__primary { + @include listItemColorModifier($c-pool, $c-hydrogen); +} + +.cf-list__secondary { + @include listItemColorModifier($c-star, $c-potassium); +} + +.cf-list__success { + @include listItemColorModifier($c-rainforest, $c-krypton); +} + +.cf-list__warning { + @include listItemColorModifier($c-pineapple, $c-sulfur); +} + +.cf-list__danger { + @include listItemColorModifier($c-curacao, $c-tungsten); +} + +/* + Size Modifiers + ------------------------------------------------------------------------------ +*/ + +@mixin listItemSizeModifier($fontSize, $height, $padding) { + &.cf-list-item, + &.cf-list-divider, + &.cf-list-link-item > a { + font-weight: $cf-font-weight--medium; + font-size: $fontSize; + padding: 0 $padding; + } + + .cf-list-item--text__no-wrap, + &.cf-list-link-item__no-wrap > a, + &.cf-list-divider { + height: $height; + line-height: $height; + } + + .cf-list-item--text__wrap, + &.cf-list-link-item__wrap > a { + padding: ceil($padding * 0.75) 0; + } + + &.cf-list-empty { + font-weight: $cf-font-weight--medium; + font-size: $fontSize; + + .cf-list-item--text__no-wrap, + .cf-list-item--text__wrap { + padding-left: $padding; + padding-right: $padding; + } + } +} + +.cf-list-item__xs { + @include listItemSizeModifier( + $cf-form-xs-font, + $cf-form-xs-height, + $cf-form-xs-padding + ); +} + +.cf-list-item__sm { + @include listItemSizeModifier( + $cf-form-sm-font, + $cf-form-sm-height, + $cf-form-sm-padding + ); +} + +.cf-list-item__md { + @include listItemSizeModifier( + $cf-form-md-font, + $cf-form-md-height, + $cf-form-md-padding + ); +} + +.cf-list-item__lg { + @include listItemSizeModifier( + $cf-form-lg-font, + $cf-form-lg-height, + $cf-form-lg-padding + ); +} diff --git a/src/Components/List/List.tsx b/src/Components/List/List.tsx new file mode 100644 index 00000000..02084b8a --- /dev/null +++ b/src/Components/List/List.tsx @@ -0,0 +1,125 @@ +// Libraries +import React, {forwardRef, RefObject, CSSProperties, ReactNode} from 'react' +import classnames from 'classnames' +import _ from 'lodash' + +// Components +import {DapperScrollbars} from '../DapperScrollbars/DapperScrollbars' + +// Types +import {StandardFunctionProps, InfluxColors} from '../../Types' + +// Styles +import './List.scss' + +export interface ListProps extends StandardFunctionProps { + /** Disable scrolling horizontally */ + noScrollX?: boolean + /** Disable scrolling vertically */ + noScrollY?: boolean + /** Automatically scroll to selected item when dropdown is opened */ + scrollToSelected?: boolean + /** Pass through ref for contents element within scrollbars */ + contentsRef?: RefObject + /** Useful for customizing appearance of the contents element within scrollbars */ + contentsStyle?: CSSProperties + /** Controls autoHide behavior of scrollbars within the menu */ + autoHideScrollbars?: boolean + /** Gradient start color */ + thumbStartColor?: string | InfluxColors + /** Gradient end color */ + thumbStopColor?: string | InfluxColors +} + +export type ListRef = HTMLDivElement +export type ListContentsRef = HTMLDivElement + +export const ListRoot = forwardRef( + ( + { + id, + style = {width: '100%'}, + testID = 'list', + children, + noScrollX = true, + noScrollY = false, + className, + contentsRef, + contentsStyle, + scrollToSelected = false, + autoHideScrollbars = false, + thumbStopColor = InfluxColors.Pool, + thumbStartColor = InfluxColors.Star, + }, + ref + ) => { + const ListClass = classnames('cf-list', { + [`${className}`]: className, + }) + + const scrollTop = calculateSelectedPosition(scrollToSelected, children) + + const scrollbarsStyle = { + width: '100%', + minWidth: '100%', + maxWidth: '100%', + height: '100%', + minHeight: '100%', + maxHeight: '100%', + } + + return ( +
+ +
+ {children} +
+
+
+ ) + } +) + +ListRoot.displayName = 'List' + +const calculateSelectedPosition = ( + scrollToSelected: boolean, + children: ReactNode +): number => { + if (!children || !scrollToSelected) { + return 0 + } + + const itemHeight = 24 + const items = React.Children.map(children, child => + _.get(child, 'props.selected', false) + ) + + if (!items) { + return 0 + } + + const itemIndex = items.findIndex(item => item === true) + + return itemHeight * itemIndex +} diff --git a/src/Components/List/ListDivider.tsx b/src/Components/List/ListDivider.tsx new file mode 100644 index 00000000..c176d790 --- /dev/null +++ b/src/Components/List/ListDivider.tsx @@ -0,0 +1,50 @@ +// Libraries +import React, {forwardRef} from 'react' +import classnames from 'classnames' + +// Types +import {StandardFunctionProps, ComponentSize} from '../../Types' + +export interface ListDividerProps extends StandardFunctionProps { + /** Text to be displayed on divider, a line will be displayed if no text is provided */ + text?: string + /** Size of this component */ + size?: ComponentSize +} + +export type ListDividerRef = HTMLDivElement + +export const ListDivider = forwardRef( + ( + { + id, + text, + size = ComponentSize.Small, + style, + testID = 'list-divider', + className, + }, + ref + ) => { + const listDividerClass = classnames('', { + 'cf-list-divider': text, + 'cf-list-divider__thin': !text, + [`cf-list-item__${size}`]: size, + [`${className}`]: className, + }) + + return ( +
+ {text} +
+ ) + } +) + +ListDivider.displayName = 'ListDivider' diff --git a/src/Components/List/ListEmpty.tsx b/src/Components/List/ListEmpty.tsx new file mode 100644 index 00000000..def43daf --- /dev/null +++ b/src/Components/List/ListEmpty.tsx @@ -0,0 +1,54 @@ +// Libraries +import React, {forwardRef} from 'react' +import classnames from 'classnames' + +// Types +import {StandardFunctionProps, ComponentSize} from '../../Types' + +export interface ListEmptyProps extends StandardFunctionProps { + /** Controls whether the text contents of this item wrap or not */ + wrapText?: boolean + /** Size of this component */ + size?: ComponentSize +} + +export type ListEmptyRef = HTMLDivElement + +export const ListEmpty = forwardRef( + ( + { + id, + size = ComponentSize.Small, + style, + testID = 'list-empty', + wrapText = false, + children, + className, + }, + ref + ) => { + const listEmptyClass = classnames('cf-list-empty', { + [`${className}`]: className, + [`cf-list-item__${size}`]: size, + }) + + const listEmptyTextClass = classnames('cf-list-empty--text', { + 'cf-list-item--text__wrap': wrapText, + 'cf-list-item--text__no-wrap': !wrapText, + }) + + return ( +
+
{children}
+
+ ) + } +) + +ListEmpty.displayName = 'ListEmpty' diff --git a/src/Components/List/ListIcon.scss b/src/Components/List/ListIcon.scss new file mode 100644 index 00000000..1db37355 --- /dev/null +++ b/src/Components/List/ListIcon.scss @@ -0,0 +1,13 @@ +@import '../../Styles/variables'; + +.cf-list-icon { + font-size: 1.125em; + + &:first-child { + margin-right: 0.666em; + } + + &:last-child { + margin-left: 0.666em; + } +} diff --git a/src/Components/List/ListIcon.tsx b/src/Components/List/ListIcon.tsx new file mode 100644 index 00000000..fa43bfb5 --- /dev/null +++ b/src/Components/List/ListIcon.tsx @@ -0,0 +1,21 @@ +import React, {FunctionComponent} from 'react' + +// Components +import {Icon} from '../Icon' + +// Types +import {IconFont} from '../../Types' + +// Styles +import './ListIcon.scss' + +export interface ListIconProps { + /** Icon to display */ + glyph: IconFont | string +} + +export const ListIcon: FunctionComponent = ({glyph}) => { + return +} + +ListIcon.displayName = 'ListIcon' diff --git a/src/Components/List/ListIndicator.scss b/src/Components/List/ListIndicator.scss new file mode 100644 index 00000000..d3e49c4d --- /dev/null +++ b/src/Components/List/ListIndicator.scss @@ -0,0 +1,230 @@ +@import '../../Styles/variables'; + +.cf-list-indicator { + position: relative; +} + +.cf-list-indicator--element { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +/* + Dot + ------------------------------------------------------------------------------ +*/ + +.cf-list-indicator__dot { + .cf-list-indicator--element { + background-color: $g20-white; + border-radius: 50%; + opacity: 0; + transform: translate(-50%, -50%) scale(0.25, 0.25); + transition: transform 0.25s ease, opacity 0.25s ease; + } + + &.cf-list-indicator__selected .cf-list-indicator--element { + transform: translate(-50%, -50%) scale(1, 1); + opacity: 1; + } +} + +/* + Checkbox + ------------------------------------------------------------------------------ +*/ + +.cf-list-indicator__checkbox { + border-radius: $cf-border; + + .cf-list-indicator--element { + border-radius: 50%; + opacity: 0; + transform: translate(-50%, -50%) scale(0.25, 0.25); + transition: transform 0.25s ease, opacity 0.25s ease; + } + + &.cf-list-indicator__selected .cf-list-indicator--element { + transform: translate(-50%, -50%) scale(1, 1); + opacity: 1; + } +} + +/* + Checkbox + ------------------------------------------------------------------------------ +*/ + +.cf-list-indicator__icon { + font-size: 1.125em; + + .cf-list-indicator--element { + transform: translate(-50%, -52%); + } +} + +/* + Size Modifiers + ------------------------------------------------------------------------------ +*/ + +@mixin listIndicatorSizeModifier($size) { + $dotSize: floor($size * 0.25); + $checkboxSize: floor($size * 0.5); + + @if $dotSize % 2 != 0 { + $dotSize: $dotSize + 1px; + } + + @if $checkboxSize % 2 != 0 { + $checkboxSize: $checkboxSize + 1px; + } + + &.cf-list-indicator__icon { + width: $checkboxSize; + height: $checkboxSize; + } + + &.cf-list-indicator__dot { + width: floor($size * 0.25); + height: $size; + } + + &.cf-list-indicator__checkbox { + width: $checkboxSize; + height: $checkboxSize; + } + + &.cf-list-indicator__icon, + &.cf-list-indicator__dot, + &.cf-list-indicator__checkbox { + &:first-child { + margin-right: floor($size * 0.25); + } + + &:last-child { + margin-left: floor($size * 0.25); + } + } + + &.cf-list-indicator__dot .cf-list-indicator--element, + &.cf-list-indicator__checkbox .cf-list-indicator--element { + width: $dotSize; + height: $dotSize; + } +} + +.cf-list-indicator__xs { + @include listIndicatorSizeModifier($cf-form-xs-height); +} + +.cf-list-indicator__sm { + @include listIndicatorSizeModifier($cf-form-sm-height); +} + +.cf-list-indicator__md { + @include listIndicatorSizeModifier($cf-form-md-height); +} + +.cf-list-indicator__lg { + @include listIndicatorSizeModifier($cf-form-lg-height); +} + +/* + Color Modifiers + ------------------------------------------------------------------------------ +*/ + +@mixin listIndicatorColorModifier($color) { + &.cf-list-indicator__dot { + .cf-list-indicator--element { + box-shadow: 0 0 4px 1px $color; + } + } + + &.cf-list-indicator__checkbox { + background-color: rgba($g0-obsidian, 0.5); + .cf-list-indicator--element { + background-color: $color; + box-shadow: 0 0 4px 1px $color; + } + } +} + +.cf-list-indicator__default { + @include listIndicatorColorModifier($g13-mist); +} + +.cf-list-indicator__primary { + @include listIndicatorColorModifier($c-pool); +} + +.cf-list-indicator__secondary { + @include listIndicatorColorModifier($c-star); +} + +.cf-list-indicator__success { + @include listIndicatorColorModifier($c-rainforest); +} + +.cf-list-indicator__warning { + @include listIndicatorColorModifier($c-pineapple); +} + +.cf-list-indicator__danger { + @include listIndicatorColorModifier($c-curacao); +} + +/* + Size Modifiers + ------------------------------------------------------------------------------ +*/ + +@mixin listItemSizeModifier($fontSize, $height, $padding) { + &.cf-list-item, + &.cf-list-divider { + font-weight: $cf-font-weight--medium; + font-size: $fontSize; + padding: 0 $padding; + } + + &.cf-list-item__no-wrap, + &.cf-list-divider { + height: $height; + line-height: $height; + } +} + +.cf-list-item__xs { + @include listItemSizeModifier( + $cf-form-xs-font, + $cf-form-xs-height, + $cf-form-xs-padding + ); +} + +.cf-list-item__sm { + @include listItemSizeModifier( + $cf-form-sm-font, + $cf-form-sm-height, + $cf-form-sm-padding + ); +} + +.cf-list-item__md { + @include listItemSizeModifier( + $cf-form-md-font, + $cf-form-md-height, + $cf-form-md-padding + ); +} + +.cf-list-item__lg { + @include listItemSizeModifier( + $cf-form-lg-font, + $cf-form-lg-height, + $cf-form-lg-padding + ); +} diff --git a/src/Components/List/ListIndicator.tsx b/src/Components/List/ListIndicator.tsx new file mode 100644 index 00000000..93de300d --- /dev/null +++ b/src/Components/List/ListIndicator.tsx @@ -0,0 +1,36 @@ +import React, {FunctionComponent, useContext} from 'react' +import classnames from 'classnames' + +// Contexts +import {ListItemContext} from './ListItem' + +// Styles +import './ListIndicator.scss' + +export type ListIndicatorType = 'checkbox' | 'dot' + +export interface ListIndicatorProps { + /** Controls appearance of indicator */ + type: ListIndicatorType +} + +export const ListIndicator: FunctionComponent = ({ + type, +}) => { + const {selected, color, size} = useContext(ListItemContext) + + const indicatorClassname = classnames('cf-list-indicator', { + 'cf-list-indicator__selected': selected, + [`cf-list-indicator__${type}`]: type, + [`cf-list-indicator__${color}`]: color, + [`cf-list-indicator__${size}`]: size, + }) + + return ( +
+
+
+ ) +} + +ListIndicator.displayName = 'ListIndicator' diff --git a/src/Components/List/ListItem.tsx b/src/Components/List/ListItem.tsx new file mode 100644 index 00000000..af71fe90 --- /dev/null +++ b/src/Components/List/ListItem.tsx @@ -0,0 +1,106 @@ +// Libraries +import React, {forwardRef, MouseEvent, createContext} from 'react' +import classnames from 'classnames' + +// Types +import {StandardFunctionProps, ComponentColor, ComponentSize} from '../../Types' + +export interface ListItemContextProps { + /** Whether or not the item should have selected styling */ + selected?: boolean + /** Colorization of this component */ + color?: ComponentColor + /** Size of this component */ + size?: ComponentSize +} + +type CombinedListItemProps = ListItemContextProps & StandardFunctionProps + +export interface ListItemProps extends CombinedListItemProps { + /** Value to be returned via the onClick function */ + value?: any + /** When a dropdown item is clicked, its `value` prop is returned via `onChange` */ + onClick?: (value?: any) => void + /** Controls whether the text contents of this item wrap or not */ + wrapText?: boolean + /** Title attribute */ + title?: string + /** Prevents any interaction with this element, including the onClick function */ + disabled?: boolean +} + +export type ListItemRef = HTMLDivElement + +export const ListItemContext = createContext({ + selected: false, + color: ComponentColor.Primary, + size: ComponentSize.Small, +}) + +export const ListItem = forwardRef( + ( + { + id, + size = ComponentSize.Small, + style, + value, + color = ComponentColor.Primary, + title, + testID = 'list-item', + onClick, + wrapText = false, + selected = false, + children, + disabled, + className, + }, + ref + ) => { + const listItemClass = classnames('cf-list-item', { + 'cf-list-item__active': selected, + [`cf-list__${color}`]: color, + [`cf-list-item__${size}`]: size, + [`${className}`]: className, + 'cf-list-item__disabled': disabled, + }) + + const listItemTextClass = classnames('cf-list-item--text', { + 'cf-list-item--text__wrap': wrapText, + 'cf-list-item--text__no-wrap': !wrapText, + }) + + const handleClick = (e: MouseEvent): void => { + e.preventDefault() + + if (onClick && !disabled) { + onClick(value) + } + } + + const formattedChildren = React.Children.map(children, child => { + if (typeof child === 'string') { + return
{child}
+ } + + return child + }) + + return ( +
+ + {formattedChildren} + +
+ ) + } +) + +ListItem.displayName = 'ListItem' diff --git a/src/Components/List/ListLinkItem.tsx b/src/Components/List/ListLinkItem.tsx new file mode 100644 index 00000000..80386238 --- /dev/null +++ b/src/Components/List/ListLinkItem.tsx @@ -0,0 +1,62 @@ +// Libraries +import React, {forwardRef} from 'react' +import classnames from 'classnames' + +// Types +import {StandardFunctionProps, ComponentSize, ComponentColor} from '../../Types' +import {ListItemContextProps, ListItemContext} from './ListItem' + +type CombinedListItemProps = ListItemContextProps & StandardFunctionProps + +export interface ListLinkItemProps extends CombinedListItemProps { + /** Controls whether the text contents of this item wrap or not */ + wrapText?: boolean + /** Renders the element in a disabled state, does not affect functionality */ + disabled?: boolean +} + +export type ListLinkItemRef = HTMLDivElement + +export const ListLinkItem = forwardRef( + ( + { + id, + size = ComponentSize.Small, + style, + color = ComponentColor.Primary, + testID = 'list-link-item', + wrapText = false, + selected = false, + children, + disabled, + className, + }, + ref + ) => { + const listLinkItemClass = classnames('cf-list-link-item', { + 'cf-list-link-item__active': selected, + [`cf-list__${color}`]: color, + [`cf-list-item__${size}`]: size, + [`${className}`]: className, + 'cf-list-link-item__wrap': wrapText, + 'cf-list-link-item__no-wrap': !wrapText, + 'cf-list-item__disabled': disabled, + }) + + return ( +
+ + {children} + +
+ ) + } +) + +ListLinkItem.displayName = 'ListLinkItem' diff --git a/src/Components/List/index.tsx b/src/Components/List/index.tsx new file mode 100644 index 00000000..9d743eaa --- /dev/null +++ b/src/Components/List/index.tsx @@ -0,0 +1,35 @@ +// Libraries +import React, {Component} from 'react' + +// Components +import {ListRoot, ListProps} from './List' +import {ListItem} from './ListItem' +import {ListEmpty} from './ListEmpty' +import {ListLinkItem} from './ListLinkItem' +import {ListDivider} from './ListDivider' +import {ListIndicator} from './ListIndicator' +import {ListIcon} from './ListIcon' + +export class List extends Component { + public static readonly displayName = 'List' + + public static List = ListRoot + public static Item = ListItem + public static Empty = ListEmpty + public static LinkItem = ListLinkItem + public static Divider = ListDivider + public static Indicator = ListIndicator + public static Icon = ListIcon + + render() { + return + } +} + +export {ListProps, ListRef} from './List' +export * from './ListItem' +export * from './ListEmpty' +export * from './ListLinkItem' +export * from './ListDivider' +export * from './ListIndicator' +export * from './ListIcon' diff --git a/src/index.ts b/src/index.ts index 1bd33c50..63453bcb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -33,6 +33,7 @@ export * from './Components/Label/index' export * from './Components/NavMenu/index' export * from './Components/Notification/index' export * from './Components/Overlay/index' +export * from './Components/List/index' export * from './Components/Logo/index' export * from './Components/Page/index' export * from './Components/Panel/index' From 5ab98ecc4d33fa9bf36a55d7945475a696259f7c Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Wed, 8 Jul 2020 16:41:52 -0700 Subject: [PATCH 02/22] feat: write documentation for List family --- src/Components/List/Documentation/List.md | 36 +++++++++++ .../List/Documentation/ListDivider.md | 25 ++++++++ src/Components/List/Documentation/ListItem.md | 61 +++++++++++++++++++ .../List/Documentation/ListLinkItem.md | 27 ++++++++ 4 files changed, 149 insertions(+) create mode 100644 src/Components/List/Documentation/List.md create mode 100644 src/Components/List/Documentation/ListDivider.md create mode 100644 src/Components/List/Documentation/ListItem.md create mode 100644 src/Components/List/Documentation/ListLinkItem.md diff --git a/src/Components/List/Documentation/List.md b/src/Components/List/Documentation/List.md new file mode 100644 index 00000000..80db99ac --- /dev/null +++ b/src/Components/List/Documentation/List.md @@ -0,0 +1,36 @@ +# List + +List is the parent component of the List Family; every member of the component family can be accessed from this single import. The List family offers a set of components that can be composed to display items in lists, hence the name. It is generic enough to work with selectable lists or static lists. `List` pairs especially well with `Popover` + +### Usage + +```tsx +import {List} from '@influxdata/clockface' +``` + +```tsx + + Hey there, + It's me + + Your pal List! + +``` + +### Scrolling + +Use the `style` prop to control scrolling. By default `List` will fill the width of its parent, but we recommend giving it at least a `width` and `height` style. + +### Customization + +List children can be customized individually and are composable to encourage extension. + +### Example + + + + + + + + diff --git a/src/Components/List/Documentation/ListDivider.md b/src/Components/List/Documentation/ListDivider.md new file mode 100644 index 00000000..64b0e4f3 --- /dev/null +++ b/src/Components/List/Documentation/ListDivider.md @@ -0,0 +1,25 @@ +# ListDivider + +`ListDivider` is part of the `List` component family and can be accessed from the single import. Hence the name this component is useful for dividing the contents of `List` into sections. + +### Usage + +```tsx +import {List, ComponentSize} from '@influxdata/clockface' +``` + +```tsx + +``` + +If the `text` prop is left empty the divider will render as a thin horizontal line. Should the specified `text` exceed the width of the `List` the divider will truncate it with ellipsis. + +### Example + + + + + + + + diff --git a/src/Components/List/Documentation/ListItem.md b/src/Components/List/Documentation/ListItem.md new file mode 100644 index 00000000..71ab7800 --- /dev/null +++ b/src/Components/List/Documentation/ListItem.md @@ -0,0 +1,61 @@ +# ListItem + +`ListItem` is part of the `List` component family and can be accessed from the single import. + +### Usage + +```tsx +import {List} from '@influxdata/clockface' +``` + +```tsx + + Banana + +``` + +### Example + + + +### Want icons, dots, or checkboxes? + +Also includes in the `List` family are a hanful of components to embellish `ListItem` with. Children of `ListItem` are laid out horizontally, so if you want something on the right side of your `ListItem` place that node last. See more examples in the **Examples** section + +Example with a checkbox: + +```tsx + + // Type accepts "checkbox" or "dot" + + Item text + +``` + +Example with an icon: + +```tsx + + + Item text + +``` + +### Gotchas + +Any child nodes of type `string` are automatically wrapped in an element intended to manage text wrapping and layout alongside indiciators + + + + + + diff --git a/src/Components/List/Documentation/ListLinkItem.md b/src/Components/List/Documentation/ListLinkItem.md new file mode 100644 index 00000000..3b8dcaf0 --- /dev/null +++ b/src/Components/List/Documentation/ListLinkItem.md @@ -0,0 +1,27 @@ +# ListLinkItem + +`ListLinkItem` is part of the `List` component family and can be accessed from the single import. It is intended as an alternative to `ListItem` when you need to use `` or `` elements instead of `onClick` behavior. + +### Usage + +```tsx +import {List} from '@influxdata/clockface' +``` + +```tsx + + Item Text + +``` + +It is not recommended to pass children other than `` or `` into this component. Any components that you would normally pass in as children pass in as children of the `` or `` element instead + +### Example + + + + + + + + From 3f34fd4e7bfedf8dc641356e47ce902f67d8a167 Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Wed, 8 Jul 2020 16:42:41 -0700 Subject: [PATCH 03/22] fix: ensure link items receive transition styles --- src/Components/List/List.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/List/List.scss b/src/Components/List/List.scss index e41446df..8562fd7b 100644 --- a/src/Components/List/List.scss +++ b/src/Components/List/List.scss @@ -108,7 +108,7 @@ $list-item--divider-text: rgba($g20-white, 0.4); @include listItemSharedStyles(); > a { - transition: color 0.25s ease, background-color 0.25s ease; + transition: color 0.25s ease, background-color 0.25s ease !important; display: inline-block; border-radius: $cf-radius-sm; } From c1ee78507b43e5f7d7639fb6885d700ef986189f Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Wed, 8 Jul 2020 16:44:57 -0700 Subject: [PATCH 04/22] feat: document value & onClick pattern --- src/Components/List/Documentation/ListItem.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Components/List/Documentation/ListItem.md b/src/Components/List/Documentation/ListItem.md index 71ab7800..727e7179 100644 --- a/src/Components/List/Documentation/ListItem.md +++ b/src/Components/List/Documentation/ListItem.md @@ -27,6 +27,10 @@ import {List} from '@influxdata/clockface' +### Handling `onClick` behavior + +Whatever you pass in to the `value` prop is passed back as an argument of the `onClick` prop. Since click interactions tend to utilize an identifier this pattern will save you the need for an extra handler function. + ### Want icons, dots, or checkboxes? Also includes in the `List` family are a hanful of components to embellish `ListItem` with. Children of `ListItem` are laid out horizontally, so if you want something on the right side of your `ListItem` place that node last. See more examples in the **Examples** section From a37f582faa6737e2ed01285bef83510625465ccc Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Fri, 10 Jul 2020 12:56:45 -0700 Subject: [PATCH 05/22] Make some color utils more flexible --- src/Utils/index.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Utils/index.ts b/src/Utils/index.ts index b7882b6e..6d546d02 100644 --- a/src/Utils/index.ts +++ b/src/Utils/index.ts @@ -28,7 +28,7 @@ export const convertCSSPropertiesToString = (styles: CSSProperties): string => }, '') export const calculateTextColorFromBackground = ( - backgroundColor: InfluxColors | string, + backgroundColor?: InfluxColors | string, gradient?: Gradients ): string => { const mediumGrey = 0.34 @@ -38,26 +38,36 @@ export const calculateTextColorFromBackground = ( return chroma(start).luminance() >= mediumGrey ? 'dark' : 'light' } - return chroma(backgroundColor).luminance() >= mediumGrey ? 'dark' : 'light' + if (backgroundColor) { + return chroma(backgroundColor).luminance() >= mediumGrey ? 'dark' : 'light' + } + + return '' } export const generateBackgroundStyle = ( - backgroundColor: InfluxColors | string, + backgroundColor?: InfluxColors | string, gradient?: Gradients, bordered?: boolean, - style?: CSSProperties + style?: CSSProperties, + angle?: number ): CSSProperties => { + if (!backgroundColor && !gradient) { + return style || {} + } + let border = `2px solid ${backgroundColor}` let panelStyle: CSSProperties = {backgroundColor} if (gradient) { const colors = getColorsFromGradient(gradient) + const gradientAngle = angle || 45 border = `2px solid ${colors.stop}` panelStyle = { ...panelStyle, - background: `linear-gradient(45deg, ${colors.start} 0%,${colors.stop} 100%)`, + background: `linear-gradient(${gradientAngle}deg, ${colors.start} 0%,${colors.stop} 100%)`, } } From 995f9fdb402c1e6f5a872014825798935a1ce228 Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Fri, 10 Jul 2020 14:47:25 -0700 Subject: [PATCH 06/22] Use backgroundColor and gradient instead of ComponentColor --- .../List/Documentation/List.stories.tsx | 295 +++++++++++++----- src/Components/List/List.scss | 178 ++++++----- src/Components/List/List.tsx | 67 +++- src/Components/List/ListDivider.tsx | 8 +- src/Components/List/ListIcon.scss | 4 +- src/Components/List/ListIndicator.scss | 38 ++- src/Components/List/ListIndicator.tsx | 22 +- src/Components/List/ListItem.tsx | 92 +++++- src/Components/List/ListItemHighlight.tsx | 47 +++ src/Components/List/ListLinkItem.tsx | 17 +- 10 files changed, 565 insertions(+), 203 deletions(-) create mode 100644 src/Components/List/ListItemHighlight.tsx diff --git a/src/Components/List/Documentation/List.stories.tsx b/src/Components/List/Documentation/List.stories.tsx index 658e9887..d39d1850 100644 --- a/src/Components/List/Documentation/List.stories.tsx +++ b/src/Components/List/Documentation/List.stories.tsx @@ -4,7 +4,14 @@ import marked from 'marked' // Storybook import {storiesOf} from '@storybook/react' -import {withKnobs, text, select, boolean, object} from '@storybook/addon-knobs' +import { + withKnobs, + text, + select, + boolean, + object, + color, +} from '@storybook/addon-knobs' import {mapEnumKeys} from '../../../Utils/storybook' import {useState} from '@storybook/addons' @@ -15,7 +22,7 @@ import { ListDividerRef, ListItemRef, ListEmptyRef, - ListLinkItemRef, + // ListLinkItemRef, } from '../' import {Popover} from '../../Popover' @@ -25,6 +32,8 @@ import { ComponentSize, IconFont, Appearance, + InfluxColors, + Gradients, } from '../../../Types' // Notes @@ -32,7 +41,7 @@ import ListReadme from './List.md' import ListDividerReadme from './ListDivider.md' import ListItemReadme from './ListItem.md' import ListEmptyReadme from './ListEmpty.md' -import ListLinkItemReadme from './ListLinkItem.md' +// import ListLinkItemReadme from './ListLinkItem.md' const listFamilyStories = storiesOf( 'Components|List/Family', @@ -44,12 +53,18 @@ const listExamplesStories = storiesOf( module ).addDecorator(withKnobs) -const defaultListStyle = {width: '250px'} +const defaultListStyle = {width: '250px', height: '600px'} interface ExampleDropdownItem { type: 'item' | 'divider' wrap: boolean text: string + disabled: boolean + backgroundColor?: InfluxColors + gradient?: Gradients + noClick?: boolean + icon?: IconFont + noIndicator?: boolean } const exampleItems: ExampleDropdownItem[] = [ @@ -57,92 +72,194 @@ const exampleItems: ExampleDropdownItem[] = [ type: 'divider', wrap: false, text: 'Domestic Fruits', + disabled: false, }, { type: 'item', wrap: false, text: 'Banana', + disabled: false, }, { type: 'item', wrap: false, text: 'Kiwi', + disabled: false, }, { type: 'item', wrap: false, text: 'Lemon', + disabled: false, + }, + { + type: 'item', + wrap: false, + text: 'Lime', + disabled: true, }, { type: 'item', wrap: false, text: 'Apple', + disabled: false, }, { type: 'item', wrap: false, text: 'Orange', + disabled: false, }, { type: 'item', wrap: false, text: 'Grapefruit', + disabled: false, }, { type: 'item', wrap: false, text: 'Pear', + disabled: false, }, { type: 'divider', wrap: false, text: 'Imported Fruits', + disabled: false, }, { type: 'item', wrap: false, text: 'Dragonfruit', + disabled: false, + backgroundColor: InfluxColors.Amethyst, }, { type: 'item', wrap: false, text: 'Yuzu', + disabled: true, + backgroundColor: InfluxColors.Thunder, }, { type: 'item', wrap: false, text: 'Mango', + disabled: false, + backgroundColor: InfluxColors.Tiger, }, { type: 'item', wrap: false, text: 'Lychee', + disabled: false, + backgroundColor: InfluxColors.Potassium, }, { type: 'item', wrap: false, - text: 'Passionfruit', + text: 'Honeydew Melon', + disabled: false, + backgroundColor: InfluxColors.Honeydew, + }, + { + type: 'divider', + wrap: false, + text: 'Gradients', + disabled: false, }, { type: 'item', wrap: false, - text: 'Guava', + text: 'PolarExpress', + disabled: false, + gradient: Gradients.PolarExpress, + }, + { + type: 'item', + wrap: false, + text: 'PastryCafe', + disabled: false, + gradient: Gradients.PastryCafe, }, { type: 'divider', wrap: false, text: '', + disabled: false, }, { type: 'item', wrap: true, text: 'This dropdown item text is much longer to test text wrapping', + disabled: false, }, { type: 'item', wrap: false, text: 'Long text that will be truncated if it exceeds the width of the List', + disabled: false, + }, + { + type: 'divider', + wrap: false, + text: 'Edge Casees', + disabled: false, + }, + { + type: 'item', + wrap: false, + text: 'Item without click handler', + disabled: false, + noClick: true, + }, + { + type: 'item', + wrap: false, + text: 'Item with Icon', + disabled: false, + icon: IconFont.Chat, + }, + { + type: 'item', + wrap: false, + text: 'Item with Icon + BackgroundColor', + disabled: false, + icon: IconFont.Chat, + backgroundColor: InfluxColors.Pool, + }, + { + type: 'item', + wrap: false, + text: 'Item with Icon + Gradient', + disabled: false, + icon: IconFont.Chat, + gradient: Gradients.SavannaHeat, + }, + { + type: 'item', + wrap: false, + text: 'Item without indicator', + disabled: false, + noIndicator: true, + }, + { + type: 'item', + wrap: false, + text: 'Item without indicator + BackgroundColor', + disabled: false, + noIndicator: true, + backgroundColor: InfluxColors.Star, + }, + { + type: 'item', + wrap: false, + text: 'Item without indicator + Gradient', + disabled: false, + noIndicator: true, + gradient: Gradients.NineteenEightyFour, }, ] @@ -164,17 +281,35 @@ listFamilyStories.add( return (
- + {exampleItems.map(item => { if (item.type === 'item') { return ( + {item.icon && } + {!item.noIndicator && !item.icon && ( + + )} {item.text} ) @@ -267,11 +413,7 @@ listFamilyStories.add( onClick={value => { alert(`onClick returned: ${value}`) }} - color={ - ComponentColor[ - select('color', mapEnumKeys(ComponentColor), 'Primary') - ] - } + backgroundColor={color('color', InfluxColors.Pool)} size={ ComponentSize[select('size', mapEnumKeys(ComponentSize), 'Small')] } @@ -327,52 +469,48 @@ listFamilyStories.add( } ) -listFamilyStories.add( - 'ListLinkItem', - () => { - const dropdownLinkItemRef: RefObject = createRef() - - const logRef = (): void => { - /* eslint-disable */ - console.log(dropdownLinkItemRef.current) - /* eslint-enable */ - } - - return ( - - ) - }, - { - readme: { - content: marked(ListLinkItemReadme), - }, - } -) +// listFamilyStories.add( +// 'ListLinkItem', +// () => { +// const dropdownLinkItemRef: RefObject = createRef() + +// const logRef = (): void => { +// /* eslint-disable */ +// console.log(dropdownLinkItemRef.current) +// /* eslint-enable */ +// } + +// return ( +//
+// +// +// {text('link text', 'Example Link')} +// +// +//
+// +//
+//
+// ) +// }, +// { +// readme: { +// content: marked(ListLinkItemReadme), +// }, +// } +// ) listExamplesStories.add( 'Icons & Indicators', @@ -395,11 +533,7 @@ listExamplesStories.add( onClick={value => { alert(`onClick returned: ${value}`) }} - color={ - ComponentColor[ - select('color', mapEnumKeys(ComponentColor), 'Primary') - ] - } + backgroundColor={color('color', InfluxColors.Pool)} size={ ComponentSize[select('size', mapEnumKeys(ComponentSize), 'Small')] } @@ -421,11 +555,7 @@ listExamplesStories.add( onClick={value => { alert(`onClick returned: ${value}`) }} - color={ - ComponentColor[ - select('color', mapEnumKeys(ComponentColor), 'Primary') - ] - } + backgroundColor={color('color', InfluxColors.Pool)} size={ ComponentSize[select('size', mapEnumKeys(ComponentSize), 'Small')] } @@ -475,11 +605,12 @@ listExamplesStories.add( Pet Turtle
( - + a:link, - &.cf-list-link-item > a:visited, - &.cf-list-link-item > a:active { - color: $color; - } - - &.cf-list-link-item > a:hover { - color: $colorHover; - background-color: rgba($g20-white, 0.05); - } - - &.cf-list-link-item__active > a:link, - &.cf-list-link-item__active > a:visited, - &.cf-list-link-item__active > a:active, - &.cf-list-link-item__active > a:hover { - color: $g20-white; - background-color: rgba($g20-white, 0.05); - } -} - -.cf-list__default { - @include listItemColorModifier($g13-mist, $g15-platinum); -} - -.cf-list__primary { - @include listItemColorModifier($c-pool, $c-hydrogen); -} - -.cf-list__secondary { - @include listItemColorModifier($c-star, $c-potassium); -} - -.cf-list__success { - @include listItemColorModifier($c-rainforest, $c-krypton); -} - -.cf-list__warning { - @include listItemColorModifier($c-pineapple, $c-sulfur); -} - -.cf-list__danger { - @include listItemColorModifier($c-curacao, $c-tungsten); -} - /* Size Modifiers ------------------------------------------------------------------------------ diff --git a/src/Components/List/List.tsx b/src/Components/List/List.tsx index 02084b8a..0b93e988 100644 --- a/src/Components/List/List.tsx +++ b/src/Components/List/List.tsx @@ -1,17 +1,43 @@ // Libraries -import React, {forwardRef, RefObject, CSSProperties, ReactNode} from 'react' +import React, { + forwardRef, + RefObject, + CSSProperties, + ReactNode, + createContext, +} from 'react' import classnames from 'classnames' import _ from 'lodash' // Components import {DapperScrollbars} from '../DapperScrollbars/DapperScrollbars' +// Utils +import { + generateBackgroundStyle, + calculateTextColorFromBackground, +} from '../../Utils' + // Types -import {StandardFunctionProps, InfluxColors} from '../../Types' +import {StandardFunctionProps, InfluxColors, Gradients} from '../../Types' // Styles import './List.scss' +export interface ListContextProps { + listBackgroundColor?: InfluxColors | string + listGradient?: Gradients + listContrastColor?: InfluxColors | string +} + +export type ListItemRef = HTMLDivElement + +export const ListContext = createContext({ + listBackgroundColor: undefined, + listGradient: undefined, + listContrastColor: undefined, +}) + export interface ListProps extends StandardFunctionProps { /** Disable scrolling horizontally */ noScrollX?: boolean @@ -29,6 +55,10 @@ export interface ListProps extends StandardFunctionProps { thumbStartColor?: string | InfluxColors /** Gradient end color */ thumbStopColor?: string | InfluxColors + /** Colorizes the background of the list */ + backgroundColor?: InfluxColors | string + /** Overrides backgroundColor, fills background with gradient */ + gradient?: Gradients } export type ListRef = HTMLDivElement @@ -41,20 +71,29 @@ export const ListRoot = forwardRef( style = {width: '100%'}, testID = 'list', children, + gradient, noScrollX = true, noScrollY = false, className, contentsRef, contentsStyle, - scrollToSelected = false, - autoHideScrollbars = false, thumbStopColor = InfluxColors.Pool, thumbStartColor = InfluxColors.Star, + backgroundColor, + scrollToSelected = false, + autoHideScrollbars = false, }, ref ) => { + const contrastColor = calculateTextColorFromBackground( + backgroundColor, + gradient + ) + const ListClass = classnames('cf-list', { [`${className}`]: className, + [`cf-list__${contrastColor || 'light'}`]: true, + 'cf-list__special-light': backgroundColor === InfluxColors.Obsidian, }) const scrollTop = calculateSelectedPosition(scrollToSelected, children) @@ -68,11 +107,19 @@ export const ListRoot = forwardRef( maxHeight: '100%', } + const listStyle = generateBackgroundStyle( + backgroundColor, + gradient, + false, + style, + 90 + ) + return (
@@ -92,7 +139,15 @@ export const ListRoot = forwardRef( className="cf-list--contents" data-testid={`${testID}--contents`} > - {children} + + {children} +
diff --git a/src/Components/List/ListDivider.tsx b/src/Components/List/ListDivider.tsx index c176d790..5c27f082 100644 --- a/src/Components/List/ListDivider.tsx +++ b/src/Components/List/ListDivider.tsx @@ -1,7 +1,10 @@ // Libraries -import React, {forwardRef} from 'react' +import React, {forwardRef, useContext} from 'react' import classnames from 'classnames' +// Contexts +import {ListContext} from './List' + // Types import {StandardFunctionProps, ComponentSize} from '../../Types' @@ -26,10 +29,13 @@ export const ListDivider = forwardRef( }, ref ) => { + const {listContrastColor} = useContext(ListContext) + const listDividerClass = classnames('', { 'cf-list-divider': text, 'cf-list-divider__thin': !text, [`cf-list-item__${size}`]: size, + [`cf-list-divider__${listContrastColor}`]: true, [`${className}`]: className, }) diff --git a/src/Components/List/ListIcon.scss b/src/Components/List/ListIcon.scss index 1db37355..e664d855 100644 --- a/src/Components/List/ListIcon.scss +++ b/src/Components/List/ListIcon.scss @@ -7,7 +7,7 @@ margin-right: 0.666em; } - &:last-child { - margin-left: 0.666em; + .cf-list-item--text + & { + margin-left: 0.5em; } } diff --git a/src/Components/List/ListIndicator.scss b/src/Components/List/ListIndicator.scss index d3e49c4d..ccf752d9 100644 --- a/src/Components/List/ListIndicator.scss +++ b/src/Components/List/ListIndicator.scss @@ -18,7 +18,6 @@ .cf-list-indicator__dot { .cf-list-indicator--element { - background-color: $g20-white; border-radius: 50%; opacity: 0; transform: translate(-50%, -50%) scale(0.25, 0.25); @@ -29,6 +28,16 @@ transform: translate(-50%, -50%) scale(1, 1); opacity: 1; } + + &.cf-list-indicator__light .cf-list-indicator--element { + background-color: $g20-white; + box-shadow: 0 0 5px 1px $g20-white; + } + + &.cf-list-indicator__dark .cf-list-indicator--element { + background-color: $g2-kevlar; + box-shadow: 0 0 5px 1px $g2-kevlar; + } } /* @@ -40,6 +49,8 @@ border-radius: $cf-border; .cf-list-indicator--element { + background-color: $c-pool; + box-shadow: 0 0 5px 1px $c-pool; border-radius: 50%; opacity: 0; transform: translate(-50%, -50%) scale(0.25, 0.25); @@ -50,6 +61,22 @@ transform: translate(-50%, -50%) scale(1, 1); opacity: 1; } + + &.cf-list-indicator__light { + background-color: rgba($g0-obsidian, 0.5); + } + + &.cf-list-indicator__light.cf-list-indicator__special-contrast { + background-color: rgba($g2-kevlar, 0.666); + } + + &.cf-list-indicator__selected.cf-list-indicator__light.cf-list-indicator__special-contrast { + background-color: rgba($g0-obsidian, 0.5); + } + + &.cf-list-indicator__dark { + background-color: rgba($g0-obsidian, 0.15); + } } /* @@ -71,7 +98,7 @@ */ @mixin listIndicatorSizeModifier($size) { - $dotSize: floor($size * 0.25); + $dotSize: floor($size * 0.15); $checkboxSize: floor($size * 0.5); @if $dotSize % 2 != 0 { @@ -88,7 +115,7 @@ } &.cf-list-indicator__dot { - width: floor($size * 0.25); + width: floor($size * 0.15); height: $size; } @@ -97,14 +124,13 @@ height: $checkboxSize; } - &.cf-list-indicator__icon, &.cf-list-indicator__dot, &.cf-list-indicator__checkbox { &:first-child { - margin-right: floor($size * 0.25); + margin-right: floor($size * 0.3); } - &:last-child { + .cf-list-item--text + & { margin-left: floor($size * 0.25); } } diff --git a/src/Components/List/ListIndicator.tsx b/src/Components/List/ListIndicator.tsx index 93de300d..978e42f5 100644 --- a/src/Components/List/ListIndicator.tsx +++ b/src/Components/List/ListIndicator.tsx @@ -3,6 +3,10 @@ import classnames from 'classnames' // Contexts import {ListItemContext} from './ListItem' +import {ListContext} from './List' + +// Types +import {InfluxColors} from '../../Types' // Styles import './ListIndicator.scss' @@ -17,18 +21,30 @@ export interface ListIndicatorProps { export const ListIndicator: FunctionComponent = ({ type, }) => { - const {selected, color, size} = useContext(ListItemContext) + const {size, selected, listItemBackgroundColor} = useContext(ListItemContext) + const {listContrastColor, listBackgroundColor} = useContext(ListContext) const indicatorClassname = classnames('cf-list-indicator', { 'cf-list-indicator__selected': selected, [`cf-list-indicator__${type}`]: type, - [`cf-list-indicator__${color}`]: color, + [`cf-list-indicator__${listContrastColor || 'light'}`]: true, + 'cf-list-indicator__special-contrast': + listBackgroundColor === InfluxColors.Obsidian, [`cf-list-indicator__${size}`]: size, }) + let elementStyle + + if (listItemBackgroundColor && type === 'checkbox') { + elementStyle = { + backgroundColor: InfluxColors.White, + boxShadow: `0 0 5px 1px ${listItemBackgroundColor}`, + } + } + return (
-
+
) } diff --git a/src/Components/List/ListItem.tsx b/src/Components/List/ListItem.tsx index af71fe90..14c1cd6c 100644 --- a/src/Components/List/ListItem.tsx +++ b/src/Components/List/ListItem.tsx @@ -1,20 +1,43 @@ // Libraries -import React, {forwardRef, MouseEvent, createContext} from 'react' +import React, {forwardRef, MouseEvent, createContext, useContext} from 'react' import classnames from 'classnames' +// Contexts +import {ListContext} from './List' + +// Components +import {ListItemHighlight} from './ListItemHighlight' + +// Utils +import {calculateTextColorFromBackground} from '../../Utils' + // Types -import {StandardFunctionProps, ComponentColor, ComponentSize} from '../../Types' +import { + StandardFunctionProps, + InfluxColors, + ComponentSize, + Gradients, +} from '../../Types' -export interface ListItemContextProps { +export interface ListItemSharedProps { /** Whether or not the item should have selected styling */ selected?: boolean - /** Colorization of this component */ - color?: ComponentColor + /** Colorizes the background of the list item in hover and selected state */ + backgroundColor?: InfluxColors | string + /** Overrides backgroundColor, fills background with gradient */ + gradient?: Gradients /** Size of this component */ size?: ComponentSize } -type CombinedListItemProps = ListItemContextProps & StandardFunctionProps +export interface ListItemContextProps + extends Pick { + listItemContrastColor?: string + listItemBackgroundColor?: InfluxColors | string + listItemGradient?: Gradients +} + +type CombinedListItemProps = ListItemSharedProps & StandardFunctionProps export interface ListItemProps extends CombinedListItemProps { /** Value to be returned via the onClick function */ @@ -33,8 +56,10 @@ export type ListItemRef = HTMLDivElement export const ListItemContext = createContext({ selected: false, - color: ComponentColor.Primary, size: ComponentSize.Small, + listItemBackgroundColor: undefined, + listItemGradient: undefined, + listItemContrastColor: undefined, }) export const ListItem = forwardRef( @@ -44,24 +69,32 @@ export const ListItem = forwardRef( size = ComponentSize.Small, style, value, - color = ComponentColor.Primary, title, testID = 'list-item', onClick, wrapText = false, selected = false, + gradient, children, - disabled, + disabled = false, className, + backgroundColor, }, ref ) => { + const {listContrastColor} = useContext(ListContext) + const contrastColor = calculateTextColorFromBackground( + backgroundColor, + gradient + ) + const listItemClass = classnames('cf-list-item', { 'cf-list-item__active': selected, - [`cf-list__${color}`]: color, [`cf-list-item__${size}`]: size, + [`cf-list-item__${listContrastColor}`]: true, [`${className}`]: className, 'cf-list-item__disabled': disabled, + 'cf-list-item__clickable': !!onClick, }) const listItemTextClass = classnames('cf-list-item--text', { @@ -85,19 +118,54 @@ export const ListItem = forwardRef( return child }) + let highlightElement + + if (!!onClick && !disabled) { + highlightElement = ( + + ) + } + + let itemStyle = style + const itemColor = + contrastColor === 'dark' ? InfluxColors.Kevlar : InfluxColors.White + + if (backgroundColor) { + itemStyle = selected + ? {color: itemColor, ...style} + : {color: backgroundColor, ...style} + } + + if (gradient) { + itemStyle = selected ? {color: itemColor, ...style} : undefined + } + return (
- + {formattedChildren} + {highlightElement}
) } diff --git a/src/Components/List/ListItemHighlight.tsx b/src/Components/List/ListItemHighlight.tsx new file mode 100644 index 00000000..b3a17078 --- /dev/null +++ b/src/Components/List/ListItemHighlight.tsx @@ -0,0 +1,47 @@ +// Libraries +import React, {FC, useContext} from 'react' +import classnames from 'classnames' + +// Contexts +import {ListContext} from './List' + +// Utils +import {generateBackgroundStyle} from '../../Utils' + +// Types +import {InfluxColors, Gradients} from '../../Types' + +export interface ListItemHighlightProps { + /** Is the parent element selected? */ + selected?: boolean + /** Colorizes the background of the list item in hover and selected state */ + backgroundColor?: InfluxColors | string + /** Overrides backgroundColor, fills background with gradient */ + gradient?: Gradients +} + +export const ListItemHighlight: FC = ({ + selected, + backgroundColor, + gradient, +}) => { + const {listContrastColor} = useContext(ListContext) + + const itemHighlightClassname = classnames('cf-list-item--highlight', { + [`cf-list-item--highlight__${listContrastColor}`]: true, + }) + + const selectedStyle = generateBackgroundStyle( + backgroundColor, + gradient, + false, + {}, + 90 + ) + + const listStyle = selected ? selectedStyle : undefined + + return
+} + +ListItemHighlight.displayName = 'ListItemHighlight' diff --git a/src/Components/List/ListLinkItem.tsx b/src/Components/List/ListLinkItem.tsx index 80386238..154c2623 100644 --- a/src/Components/List/ListLinkItem.tsx +++ b/src/Components/List/ListLinkItem.tsx @@ -2,11 +2,14 @@ import React, {forwardRef} from 'react' import classnames from 'classnames' +// Components +import {ListItemHighlight} from './ListItemHighlight' + // Types -import {StandardFunctionProps, ComponentSize, ComponentColor} from '../../Types' -import {ListItemContextProps, ListItemContext} from './ListItem' +import {StandardFunctionProps, ComponentSize} from '../../Types' +import {ListItemSharedProps, ListItemContext} from './ListItem' -type CombinedListItemProps = ListItemContextProps & StandardFunctionProps +type CombinedListItemProps = ListItemSharedProps & StandardFunctionProps export interface ListLinkItemProps extends CombinedListItemProps { /** Controls whether the text contents of this item wrap or not */ @@ -23,7 +26,7 @@ export const ListLinkItem = forwardRef( id, size = ComponentSize.Small, style, - color = ComponentColor.Primary, + backgroundColor, testID = 'list-link-item', wrapText = false, selected = false, @@ -35,7 +38,6 @@ export const ListLinkItem = forwardRef( ) => { const listLinkItemClass = classnames('cf-list-link-item', { 'cf-list-link-item__active': selected, - [`cf-list__${color}`]: color, [`cf-list-item__${size}`]: size, [`${className}`]: className, 'cf-list-link-item__wrap': wrapText, @@ -51,9 +53,12 @@ export const ListLinkItem = forwardRef( className={listLinkItemClass} data-testid={testID} > - + {children} +
) } From 2b23ecd5f21f0d138627cf5e63b26d031ba6869c Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Fri, 10 Jul 2020 14:56:41 -0700 Subject: [PATCH 07/22] Cleanup --- src/Components/List/ListIndicator.scss | 97 -------------------------- 1 file changed, 97 deletions(-) diff --git a/src/Components/List/ListIndicator.scss b/src/Components/List/ListIndicator.scss index ccf752d9..60d43a54 100644 --- a/src/Components/List/ListIndicator.scss +++ b/src/Components/List/ListIndicator.scss @@ -157,100 +157,3 @@ .cf-list-indicator__lg { @include listIndicatorSizeModifier($cf-form-lg-height); } - -/* - Color Modifiers - ------------------------------------------------------------------------------ -*/ - -@mixin listIndicatorColorModifier($color) { - &.cf-list-indicator__dot { - .cf-list-indicator--element { - box-shadow: 0 0 4px 1px $color; - } - } - - &.cf-list-indicator__checkbox { - background-color: rgba($g0-obsidian, 0.5); - .cf-list-indicator--element { - background-color: $color; - box-shadow: 0 0 4px 1px $color; - } - } -} - -.cf-list-indicator__default { - @include listIndicatorColorModifier($g13-mist); -} - -.cf-list-indicator__primary { - @include listIndicatorColorModifier($c-pool); -} - -.cf-list-indicator__secondary { - @include listIndicatorColorModifier($c-star); -} - -.cf-list-indicator__success { - @include listIndicatorColorModifier($c-rainforest); -} - -.cf-list-indicator__warning { - @include listIndicatorColorModifier($c-pineapple); -} - -.cf-list-indicator__danger { - @include listIndicatorColorModifier($c-curacao); -} - -/* - Size Modifiers - ------------------------------------------------------------------------------ -*/ - -@mixin listItemSizeModifier($fontSize, $height, $padding) { - &.cf-list-item, - &.cf-list-divider { - font-weight: $cf-font-weight--medium; - font-size: $fontSize; - padding: 0 $padding; - } - - &.cf-list-item__no-wrap, - &.cf-list-divider { - height: $height; - line-height: $height; - } -} - -.cf-list-item__xs { - @include listItemSizeModifier( - $cf-form-xs-font, - $cf-form-xs-height, - $cf-form-xs-padding - ); -} - -.cf-list-item__sm { - @include listItemSizeModifier( - $cf-form-sm-font, - $cf-form-sm-height, - $cf-form-sm-padding - ); -} - -.cf-list-item__md { - @include listItemSizeModifier( - $cf-form-md-font, - $cf-form-md-height, - $cf-form-md-padding - ); -} - -.cf-list-item__lg { - @include listItemSizeModifier( - $cf-form-lg-font, - $cf-form-lg-height, - $cf-form-lg-padding - ); -} From ab82afc7201e910a36bce29af5866fc6e9e82996 Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Tue, 14 Jul 2020 11:10:03 -0700 Subject: [PATCH 08/22] formatting --- src/Components/List/List.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Components/List/List.tsx b/src/Components/List/List.tsx index 0b93e988..9423d294 100644 --- a/src/Components/List/List.tsx +++ b/src/Components/List/List.tsx @@ -90,7 +90,7 @@ export const ListRoot = forwardRef( gradient ) - const ListClass = classnames('cf-list', { + const listClass = classnames('cf-list', { [`${className}`]: className, [`cf-list__${contrastColor || 'light'}`]: true, 'cf-list__special-light': backgroundColor === InfluxColors.Obsidian, @@ -120,7 +120,7 @@ export const ListRoot = forwardRef( id={id} ref={ref} style={listStyle} - className={ListClass} + className={listClass} data-testid={testID} > Date: Wed, 15 Jul 2020 10:04:25 -0700 Subject: [PATCH 09/22] Ensure gradient list items with checkboxes are styled according to gradient colors --- src/Components/List/ListIndicator.tsx | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Components/List/ListIndicator.tsx b/src/Components/List/ListIndicator.tsx index 978e42f5..d6cd227f 100644 --- a/src/Components/List/ListIndicator.tsx +++ b/src/Components/List/ListIndicator.tsx @@ -8,6 +8,9 @@ import {ListContext} from './List' // Types import {InfluxColors} from '../../Types' +// Utils +import {getColorsFromGradient} from '../../Utils/colors' + // Styles import './ListIndicator.scss' @@ -21,7 +24,12 @@ export interface ListIndicatorProps { export const ListIndicator: FunctionComponent = ({ type, }) => { - const {size, selected, listItemBackgroundColor} = useContext(ListItemContext) + const { + size, + selected, + listItemBackgroundColor, + listItemGradient, + } = useContext(ListItemContext) const {listContrastColor, listBackgroundColor} = useContext(ListContext) const indicatorClassname = classnames('cf-list-indicator', { @@ -42,6 +50,15 @@ export const ListIndicator: FunctionComponent = ({ } } + if (listItemGradient && type === 'checkbox') { + const gradientColors = getColorsFromGradient(listItemGradient) + + elementStyle = { + backgroundColor: InfluxColors.White, + boxShadow: `0 0 5px 1px ${gradientColors.stop}`, + } + } + return (
From ad17e4a07495517469a42228da455e0bd738a9c4 Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Wed, 15 Jul 2020 10:25:45 -0700 Subject: [PATCH 10/22] Update docs for List + Popover example --- .../List/Documentation/List.stories.tsx | 3 ++- src/Components/List/Documentation/ListPopover.md | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 src/Components/List/Documentation/ListPopover.md diff --git a/src/Components/List/Documentation/List.stories.tsx b/src/Components/List/Documentation/List.stories.tsx index d39d1850..aa149ed5 100644 --- a/src/Components/List/Documentation/List.stories.tsx +++ b/src/Components/List/Documentation/List.stories.tsx @@ -42,6 +42,7 @@ import ListDividerReadme from './ListDivider.md' import ListItemReadme from './ListItem.md' import ListEmptyReadme from './ListEmpty.md' // import ListLinkItemReadme from './ListLinkItem.md' +import ListPopoverReadme from './ListPopover.md' const listFamilyStories = storiesOf( 'Components|List/Family', @@ -759,7 +760,7 @@ listExamplesStories.add( }, { readme: { - content: marked(ListItemReadme), + content: marked(ListPopoverReadme), }, } ) diff --git a/src/Components/List/Documentation/ListPopover.md b/src/Components/List/Documentation/ListPopover.md new file mode 100644 index 00000000..84390fc3 --- /dev/null +++ b/src/Components/List/Documentation/ListPopover.md @@ -0,0 +1,15 @@ +# List + Popover + +Here's an example that uses the `List` component family inside a `Popover` component. The `backgroundColor` prop has been changed to a light color to exemplify how the child components respond. + +### Usage + +```tsx +import {List, Popover} from '@influxdata/clockface' +``` + + + + + + From 86d997123e3f44e71b22ff150a1736a2e9f0fb16 Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Wed, 15 Jul 2020 10:53:43 -0700 Subject: [PATCH 11/22] Use linkElement prop instead of separate component for links --- .../List/Documentation/List.stories.tsx | 90 ++++++++++--------- .../List/Documentation/ListLinkElement.md | 40 +++++++++ src/Components/List/ListItem.tsx | 63 +++++++++---- src/Components/List/ListLinkItem.tsx | 67 -------------- src/Components/List/index.tsx | 3 - 5 files changed, 132 insertions(+), 131 deletions(-) create mode 100644 src/Components/List/Documentation/ListLinkElement.md delete mode 100644 src/Components/List/ListLinkItem.tsx diff --git a/src/Components/List/Documentation/List.stories.tsx b/src/Components/List/Documentation/List.stories.tsx index aa149ed5..02e12474 100644 --- a/src/Components/List/Documentation/List.stories.tsx +++ b/src/Components/List/Documentation/List.stories.tsx @@ -41,7 +41,7 @@ import ListReadme from './List.md' import ListDividerReadme from './ListDivider.md' import ListItemReadme from './ListItem.md' import ListEmptyReadme from './ListEmpty.md' -// import ListLinkItemReadme from './ListLinkItem.md' +import ListLinkElementReadme from './ListLinkElement.md' import ListPopoverReadme from './ListPopover.md' const listFamilyStories = storiesOf( @@ -470,48 +470,52 @@ listFamilyStories.add( } ) -// listFamilyStories.add( -// 'ListLinkItem', -// () => { -// const dropdownLinkItemRef: RefObject = createRef() - -// const logRef = (): void => { -// /* eslint-disable */ -// console.log(dropdownLinkItemRef.current) -// /* eslint-enable */ -// } - -// return ( -//
-// -// -// {text('link text', 'Example Link')} -// -// -//
-// -//
-//
-// ) -// }, -// { -// readme: { -// content: marked(ListLinkItemReadme), -// }, -// } -// ) +listExamplesStories.add( + 'Using LinkElement Prop', + () => { + const linkElementRef: RefObject = createRef() + + const logRef = (): void => { + /* eslint-disable */ + console.log(linkElementRef.current) + /* eslint-enable */ + } + + return ( +
+ + } + > + + {text('text', 'Example Link')} + +
+ +
+
+ ) + }, + { + readme: { + content: marked(ListLinkElementReadme), + }, + } +) listExamplesStories.add( 'Icons & Indicators', diff --git a/src/Components/List/Documentation/ListLinkElement.md b/src/Components/List/Documentation/ListLinkElement.md new file mode 100644 index 00000000..3c100922 --- /dev/null +++ b/src/Components/List/Documentation/ListLinkElement.md @@ -0,0 +1,40 @@ +# ListItem with `linkElement` Prop + +In some cases using `onClick` to handle click behavior is not ideal and you may want to use a link based approach. The `linkElement` prop is for just that use case. + +### Usage + +```tsx +import {List} from '@influxdata/clockface' +``` + +```tsx +} +> + Banana + +``` + +For best results do not pass an an element into `linkElement` with children. It is intended to replace the `
` as the outermost element while maintaining the same children already passed in to `ListItem`. If you need to attach a `ref` that is one of the only cases to use attributes of the passed in element. + +### Example + + + +### Gotchas + +Because the element passed in to `linkElement` is cloned with a handful of props from `ListItem`, you may see attributes on the element getting replaced by attributes from `ListItem`. Please use `ListItem` as the source of truth for all attributes, if possible. + + + + + + diff --git a/src/Components/List/ListItem.tsx b/src/Components/List/ListItem.tsx index 14c1cd6c..1ca9c3f6 100644 --- a/src/Components/List/ListItem.tsx +++ b/src/Components/List/ListItem.tsx @@ -1,5 +1,11 @@ // Libraries -import React, {forwardRef, MouseEvent, createContext, useContext} from 'react' +import React, { + forwardRef, + MouseEvent, + createContext, + useContext, + cloneElement, +} from 'react' import classnames from 'classnames' // Contexts @@ -22,16 +28,11 @@ import { export interface ListItemSharedProps { /** Whether or not the item should have selected styling */ selected?: boolean - /** Colorizes the background of the list item in hover and selected state */ - backgroundColor?: InfluxColors | string - /** Overrides backgroundColor, fills background with gradient */ - gradient?: Gradients /** Size of this component */ size?: ComponentSize } -export interface ListItemContextProps - extends Pick { +export interface ListItemContextProps extends ListItemSharedProps { listItemContrastColor?: string listItemBackgroundColor?: InfluxColors | string listItemGradient?: Gradients @@ -50,6 +51,12 @@ export interface ListItemProps extends CombinedListItemProps { title?: string /** Prevents any interaction with this element, including the onClick function */ disabled?: boolean + /** Pass in an or element as an alternative to onClick */ + linkElement?: JSX.Element + /** Colorizes the background of the list item in hover and selected state */ + backgroundColor?: InfluxColors | string + /** Overrides backgroundColor, fills background with gradient */ + gradient?: Gradients } export type ListItemRef = HTMLDivElement @@ -78,6 +85,7 @@ export const ListItem = forwardRef( children, disabled = false, className, + linkElement, backgroundColor, }, ref @@ -120,7 +128,7 @@ export const ListItem = forwardRef( let highlightElement - if (!!onClick && !disabled) { + if ((!!onClick || linkElement) && !disabled) { highlightElement = ( ( itemStyle = selected ? {color: itemColor, ...style} : undefined } - return ( -
+ const listItemChildren = ( + <> ( {formattedChildren} {highlightElement} + + ) + + if (linkElement) { + return cloneElement( + linkElement, + { + style: itemStyle, + title: title, + className: listItemClass, + 'data-testid': testID, + }, + listItemChildren + ) + } + + return ( +
+ {listItemChildren}
) } diff --git a/src/Components/List/ListLinkItem.tsx b/src/Components/List/ListLinkItem.tsx deleted file mode 100644 index 154c2623..00000000 --- a/src/Components/List/ListLinkItem.tsx +++ /dev/null @@ -1,67 +0,0 @@ -// Libraries -import React, {forwardRef} from 'react' -import classnames from 'classnames' - -// Components -import {ListItemHighlight} from './ListItemHighlight' - -// Types -import {StandardFunctionProps, ComponentSize} from '../../Types' -import {ListItemSharedProps, ListItemContext} from './ListItem' - -type CombinedListItemProps = ListItemSharedProps & StandardFunctionProps - -export interface ListLinkItemProps extends CombinedListItemProps { - /** Controls whether the text contents of this item wrap or not */ - wrapText?: boolean - /** Renders the element in a disabled state, does not affect functionality */ - disabled?: boolean -} - -export type ListLinkItemRef = HTMLDivElement - -export const ListLinkItem = forwardRef( - ( - { - id, - size = ComponentSize.Small, - style, - backgroundColor, - testID = 'list-link-item', - wrapText = false, - selected = false, - children, - disabled, - className, - }, - ref - ) => { - const listLinkItemClass = classnames('cf-list-link-item', { - 'cf-list-link-item__active': selected, - [`cf-list-item__${size}`]: size, - [`${className}`]: className, - 'cf-list-link-item__wrap': wrapText, - 'cf-list-link-item__no-wrap': !wrapText, - 'cf-list-item__disabled': disabled, - }) - - return ( -
- - {children} - - -
- ) - } -) - -ListLinkItem.displayName = 'ListLinkItem' diff --git a/src/Components/List/index.tsx b/src/Components/List/index.tsx index 9d743eaa..bb71c475 100644 --- a/src/Components/List/index.tsx +++ b/src/Components/List/index.tsx @@ -5,7 +5,6 @@ import React, {Component} from 'react' import {ListRoot, ListProps} from './List' import {ListItem} from './ListItem' import {ListEmpty} from './ListEmpty' -import {ListLinkItem} from './ListLinkItem' import {ListDivider} from './ListDivider' import {ListIndicator} from './ListIndicator' import {ListIcon} from './ListIcon' @@ -16,7 +15,6 @@ export class List extends Component { public static List = ListRoot public static Item = ListItem public static Empty = ListEmpty - public static LinkItem = ListLinkItem public static Divider = ListDivider public static Indicator = ListIndicator public static Icon = ListIcon @@ -29,7 +27,6 @@ export class List extends Component { export {ListProps, ListRef} from './List' export * from './ListItem' export * from './ListEmpty' -export * from './ListLinkItem' export * from './ListDivider' export * from './ListIndicator' export * from './ListIcon' From 69e73aee46af6b5aafa86680f2e46876483a4e50 Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Wed, 15 Jul 2020 10:54:09 -0700 Subject: [PATCH 12/22] Ensure list item dots contrast consistently as text --- src/Components/List/ListIndicator.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Components/List/ListIndicator.tsx b/src/Components/List/ListIndicator.tsx index d6cd227f..8d7b6d4b 100644 --- a/src/Components/List/ListIndicator.tsx +++ b/src/Components/List/ListIndicator.tsx @@ -29,6 +29,7 @@ export const ListIndicator: FunctionComponent = ({ selected, listItemBackgroundColor, listItemGradient, + listItemContrastColor, } = useContext(ListItemContext) const {listContrastColor, listBackgroundColor} = useContext(ListContext) @@ -59,6 +60,13 @@ export const ListIndicator: FunctionComponent = ({ } } + if (listItemContrastColor === 'dark' && type === 'dot') { + elementStyle = { + backgroundColor: InfluxColors.Kevlar, + boxShadow: `0 0 5px 1px ${InfluxColors.Kevlar}`, + } + } + return (
From 0a5ac75eeb570e7beba826ae63c79b545b479666 Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Wed, 15 Jul 2020 14:17:34 -0700 Subject: [PATCH 13/22] Enable specification of maxHeight in List --- src/Components/List/List.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Components/List/List.tsx b/src/Components/List/List.tsx index 9423d294..27d8f953 100644 --- a/src/Components/List/List.tsx +++ b/src/Components/List/List.tsx @@ -59,6 +59,8 @@ export interface ListProps extends StandardFunctionProps { backgroundColor?: InfluxColors | string /** Overrides backgroundColor, fills background with gradient */ gradient?: Gradients + /** Pixel height after which the list will scroll */ + maxHeight?: string } export type ListRef = HTMLDivElement @@ -74,6 +76,7 @@ export const ListRoot = forwardRef( gradient, noScrollX = true, noScrollY = false, + maxHeight = '100%', className, contentsRef, contentsStyle, @@ -104,7 +107,7 @@ export const ListRoot = forwardRef( maxWidth: '100%', height: '100%', minHeight: '100%', - maxHeight: '100%', + maxHeight, } const listStyle = generateBackgroundStyle( From 8ee6067f9fdedaafee825929f3ee504ec37c8e26 Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Wed, 15 Jul 2020 14:29:10 -0700 Subject: [PATCH 14/22] Changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f9c10eb..0bf7ea6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ #### 2.2.2 (Unreleased) +- [#516](https://github.com/influxdata/clockface/pull/516): Introduce `List` component family + #### 2.2.1 (2020-06-11) - [#508](https://github.com/influxdata/clockface/pull/508): Add `testID` to thumb elements in `DapperScrollbars` From 1506da04e5b598a8dd7a5a3a0df5a22d810bfe59 Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Tue, 21 Jul 2020 09:41:04 -0700 Subject: [PATCH 15/22] Remove unused documentation --- .../List/Documentation/List.stories.tsx | 9 +------ .../List/Documentation/ListLinkItem.md | 27 ------------------- 2 files changed, 1 insertion(+), 35 deletions(-) delete mode 100644 src/Components/List/Documentation/ListLinkItem.md diff --git a/src/Components/List/Documentation/List.stories.tsx b/src/Components/List/Documentation/List.stories.tsx index 02e12474..f7f539f0 100644 --- a/src/Components/List/Documentation/List.stories.tsx +++ b/src/Components/List/Documentation/List.stories.tsx @@ -16,14 +16,7 @@ import {mapEnumKeys} from '../../../Utils/storybook' import {useState} from '@storybook/addons' // Components -import { - List, - ListRef, - ListDividerRef, - ListItemRef, - ListEmptyRef, - // ListLinkItemRef, -} from '../' +import {List, ListRef, ListDividerRef, ListItemRef, ListEmptyRef} from '../' import {Popover} from '../../Popover' // Types diff --git a/src/Components/List/Documentation/ListLinkItem.md b/src/Components/List/Documentation/ListLinkItem.md deleted file mode 100644 index 3b8dcaf0..00000000 --- a/src/Components/List/Documentation/ListLinkItem.md +++ /dev/null @@ -1,27 +0,0 @@ -# ListLinkItem - -`ListLinkItem` is part of the `List` component family and can be accessed from the single import. It is intended as an alternative to `ListItem` when you need to use `` or `` elements instead of `onClick` behavior. - -### Usage - -```tsx -import {List} from '@influxdata/clockface' -``` - -```tsx - - Item Text - -``` - -It is not recommended to pass children other than `` or `` into this component. Any components that you would normally pass in as children pass in as children of the `` or `` element instead - -### Example - - - - - - - - From 656dd0bedb258f499d4067125d0891cd902395f4 Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Tue, 21 Jul 2020 09:44:35 -0700 Subject: [PATCH 16/22] Rename "ListEmpty" to "ListEmptyState" --- .../List/Documentation/List.stories.tsx | 24 +++++++++++------- .../List/Documentation/ListEmpty.md | 25 ------------------- .../List/Documentation/ListEmptyState.md | 25 +++++++++++++++++++ src/Components/List/List.scss | 4 +-- .../{ListEmpty.tsx => ListEmptyState.tsx} | 21 +++++++++------- src/Components/List/index.tsx | 6 ++--- 6 files changed, 57 insertions(+), 48 deletions(-) delete mode 100644 src/Components/List/Documentation/ListEmpty.md create mode 100644 src/Components/List/Documentation/ListEmptyState.md rename src/Components/List/{ListEmpty.tsx => ListEmptyState.tsx} (59%) diff --git a/src/Components/List/Documentation/List.stories.tsx b/src/Components/List/Documentation/List.stories.tsx index f7f539f0..267fd117 100644 --- a/src/Components/List/Documentation/List.stories.tsx +++ b/src/Components/List/Documentation/List.stories.tsx @@ -16,7 +16,13 @@ import {mapEnumKeys} from '../../../Utils/storybook' import {useState} from '@storybook/addons' // Components -import {List, ListRef, ListDividerRef, ListItemRef, ListEmptyRef} from '../' +import { + List, + ListRef, + ListDividerRef, + ListItemRef, + ListEmptyStateRef, +} from '../' import {Popover} from '../../Popover' // Types @@ -33,7 +39,7 @@ import { import ListReadme from './List.md' import ListDividerReadme from './ListDivider.md' import ListItemReadme from './ListItem.md' -import ListEmptyReadme from './ListEmpty.md' +import ListEmptyStateReadme from './ListEmptyState.md' import ListLinkElementReadme from './ListLinkElement.md' import ListPopoverReadme from './ListPopover.md' @@ -429,27 +435,27 @@ listFamilyStories.add( ) listFamilyStories.add( - 'ListEmpty', + 'ListEmptyState', () => { - const listEmptyRef: RefObject = createRef() + const listEmptyStateRef: RefObject = createRef() const logRef = (): void => { /* eslint-disable */ - console.log(listEmptyRef.current) + console.log(listEmptyStateRef.current) /* eslint-enable */ } return (
- {text('children (text)', 'No items to display')} - +
@@ -458,7 +464,7 @@ listFamilyStories.add( }, { readme: { - content: marked(ListEmptyReadme), + content: marked(ListEmptyStateReadme), }, } ) diff --git a/src/Components/List/Documentation/ListEmpty.md b/src/Components/List/Documentation/ListEmpty.md deleted file mode 100644 index 8cae94e6..00000000 --- a/src/Components/List/Documentation/ListEmpty.md +++ /dev/null @@ -1,25 +0,0 @@ -# ListEmpty - -`ListEmpty` is part of the `List` component family and can be accessed from the single import. Use this component to indicate that a list does not have any items. - -### Usage - -```tsx -import {List, ComponentSize} from '@influxdata/clockface' -``` - -```tsx - - This list has no items - -``` - -### Example - - - - - - - - diff --git a/src/Components/List/Documentation/ListEmptyState.md b/src/Components/List/Documentation/ListEmptyState.md new file mode 100644 index 00000000..4cb1ba04 --- /dev/null +++ b/src/Components/List/Documentation/ListEmptyState.md @@ -0,0 +1,25 @@ +# ListEmptyState + +`ListEmptyState` is part of the `List` component family and can be accessed from the single import. Use this component to indicate that a list does not have any items. + +### Usage + +```tsx +import {List, ComponentSize} from '@influxdata/clockface' +``` + +```tsx + + This list has no items + +``` + +### Example + + + + + + + + diff --git a/src/Components/List/List.scss b/src/Components/List/List.scss index 09ba0cbc..ce07c89d 100644 --- a/src/Components/List/List.scss +++ b/src/Components/List/List.scss @@ -213,7 +213,7 @@ $list-item--background-dark: rgba($g0-obsidian, 0.09); ------------------------------------------------------------------------------ */ -.cf-list-empty { +.cf-list-empty-state { color: $g20-white; text-align: center; font-style: italic; @@ -247,7 +247,7 @@ $list-item--background-dark: rgba($g0-obsidian, 0.09); padding: ceil($padding * 0.75) 0; } - &.cf-list-empty { + &.cf-list-empty-state { font-weight: $cf-font-weight--medium; font-size: $fontSize; diff --git a/src/Components/List/ListEmpty.tsx b/src/Components/List/ListEmptyState.tsx similarity index 59% rename from src/Components/List/ListEmpty.tsx rename to src/Components/List/ListEmptyState.tsx index def43daf..1cb2523e 100644 --- a/src/Components/List/ListEmpty.tsx +++ b/src/Components/List/ListEmptyState.tsx @@ -5,34 +5,37 @@ import classnames from 'classnames' // Types import {StandardFunctionProps, ComponentSize} from '../../Types' -export interface ListEmptyProps extends StandardFunctionProps { +export interface ListEmptyStateProps extends StandardFunctionProps { /** Controls whether the text contents of this item wrap or not */ wrapText?: boolean /** Size of this component */ size?: ComponentSize } -export type ListEmptyRef = HTMLDivElement +export type ListEmptyStateRef = HTMLDivElement -export const ListEmpty = forwardRef( +export const ListEmptyState = forwardRef< + ListEmptyStateRef, + ListEmptyStateProps +>( ( { id, size = ComponentSize.Small, style, - testID = 'list-empty', + testID = 'list-empty-state', wrapText = false, children, className, }, ref ) => { - const listEmptyClass = classnames('cf-list-empty', { + const listEmptyStateClass = classnames('cf-list-empty-state', { [`${className}`]: className, [`cf-list-item__${size}`]: size, }) - const listEmptyTextClass = classnames('cf-list-empty--text', { + const listEmptyStateTextClass = classnames('cf-list-empty-state--text', { 'cf-list-item--text__wrap': wrapText, 'cf-list-item--text__no-wrap': !wrapText, }) @@ -43,12 +46,12 @@ export const ListEmpty = forwardRef( ref={ref} style={style} data-testid={testID} - className={listEmptyClass} + className={listEmptyStateClass} > -
{children}
+
{children}
) } ) -ListEmpty.displayName = 'ListEmpty' +ListEmptyState.displayName = 'ListEmptyState' diff --git a/src/Components/List/index.tsx b/src/Components/List/index.tsx index bb71c475..6f27435c 100644 --- a/src/Components/List/index.tsx +++ b/src/Components/List/index.tsx @@ -4,7 +4,7 @@ import React, {Component} from 'react' // Components import {ListRoot, ListProps} from './List' import {ListItem} from './ListItem' -import {ListEmpty} from './ListEmpty' +import {ListEmptyState} from './ListEmptyState' import {ListDivider} from './ListDivider' import {ListIndicator} from './ListIndicator' import {ListIcon} from './ListIcon' @@ -14,7 +14,7 @@ export class List extends Component { public static List = ListRoot public static Item = ListItem - public static Empty = ListEmpty + public static EmptyState = ListEmptyState public static Divider = ListDivider public static Indicator = ListIndicator public static Icon = ListIcon @@ -26,7 +26,7 @@ export class List extends Component { export {ListProps, ListRef} from './List' export * from './ListItem' -export * from './ListEmpty' +export * from './ListEmptyState' export * from './ListDivider' export * from './ListIndicator' export * from './ListIcon' From 3115796aa7ada0eced930a5375c295e14161c4de Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Tue, 21 Jul 2020 09:46:13 -0700 Subject: [PATCH 17/22] Make opacity into a variable --- src/Components/List/List.scss | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Components/List/List.scss b/src/Components/List/List.scss index ce07c89d..d01c4963 100644 --- a/src/Components/List/List.scss +++ b/src/Components/List/List.scss @@ -1,5 +1,7 @@ @import '../../Styles/variables'; +$list-item--text-opacity: 0.666; + $list-item--divider-color--light: rgba($g20-white, 0.03); $list-item--divider-text--light: rgba($g20-white, 0.4); $list-item--background-light: rgba($g20-white, 0.09); @@ -96,7 +98,7 @@ $list-item--background-dark: rgba($g0-obsidian, 0.09); } .cf-list-item__light { - color: rgba($g20-white, 0.666); + color: rgba($g20-white, $list-item--text-opacity); } .cf-list-item__light.cf-list-item__active, .cf-list-item__light.cf-list-item__active:hover { @@ -104,7 +106,7 @@ $list-item--background-dark: rgba($g0-obsidian, 0.09); } .cf-list-item__dark { - color: rgba($g2-kevlar, 0.666); + color: rgba($g2-kevlar, $list-item--text-opacity); } .cf-list-item__dark.cf-list-item__active, .cf-list-item__dark.cf-list-item__active:hover { From 5bbebf2d5d59b164653b90f725677ee866cce078 Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Tue, 21 Jul 2020 09:58:47 -0700 Subject: [PATCH 18/22] Remove unused styles --- src/Components/List/List.scss | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/Components/List/List.scss b/src/Components/List/List.scss index d01c4963..85b8ecf3 100644 --- a/src/Components/List/List.scss +++ b/src/Components/List/List.scss @@ -180,21 +180,6 @@ $list-item--background-dark: rgba($g0-obsidian, 0.09); } } -/* - Link Item - ------------------------------------------------------------------------------ -*/ - -.cf-list-link-item { - @include listItemSharedStyles(); - - > a { - transition: color 0.25s ease, background-color 0.25s ease !important; - display: inline-block; - border-radius: $cf-radius-sm; - } -} - /* Disabled State Item ------------------------------------------------------------------------------ From 5617a9c1943f6559df9a0677631daeb9a64004a0 Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Tue, 21 Jul 2020 09:58:59 -0700 Subject: [PATCH 19/22] Ensure list items always have a highlight color --- src/Components/List/Documentation/List.stories.tsx | 2 +- src/Components/List/ListItem.tsx | 2 +- src/Components/List/ListItemHighlight.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Components/List/Documentation/List.stories.tsx b/src/Components/List/Documentation/List.stories.tsx index 267fd117..620887a7 100644 --- a/src/Components/List/Documentation/List.stories.tsx +++ b/src/Components/List/Documentation/List.stories.tsx @@ -486,7 +486,7 @@ listExamplesStories.add( selected={boolean('selected', false)} wrapText={boolean('wrapText', false)} disabled={boolean('disabled', false)} - backgroundColor={color('color', InfluxColors.Pool)} + backgroundColor={color('color', InfluxColors.Star)} size={ ComponentSize[select('size', mapEnumKeys(ComponentSize), 'Small')] } diff --git a/src/Components/List/ListItem.tsx b/src/Components/List/ListItem.tsx index 1ca9c3f6..33e4bc96 100644 --- a/src/Components/List/ListItem.tsx +++ b/src/Components/List/ListItem.tsx @@ -99,7 +99,7 @@ export const ListItem = forwardRef( const listItemClass = classnames('cf-list-item', { 'cf-list-item__active': selected, [`cf-list-item__${size}`]: size, - [`cf-list-item__${listContrastColor}`]: true, + [`cf-list-item__${listContrastColor || 'light'}`]: true, [`${className}`]: className, 'cf-list-item__disabled': disabled, 'cf-list-item__clickable': !!onClick, diff --git a/src/Components/List/ListItemHighlight.tsx b/src/Components/List/ListItemHighlight.tsx index b3a17078..886f18c6 100644 --- a/src/Components/List/ListItemHighlight.tsx +++ b/src/Components/List/ListItemHighlight.tsx @@ -28,7 +28,7 @@ export const ListItemHighlight: FC = ({ const {listContrastColor} = useContext(ListContext) const itemHighlightClassname = classnames('cf-list-item--highlight', { - [`cf-list-item--highlight__${listContrastColor}`]: true, + [`cf-list-item--highlight__${listContrastColor || 'light'}`]: true, }) const selectedStyle = generateBackgroundStyle( From a7dac67a79464906b299a2e7258fcb44a4a989ca Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Tue, 21 Jul 2020 10:01:17 -0700 Subject: [PATCH 20/22] Include id attribute when cloning --- src/Components/List/ListItem.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Components/List/ListItem.tsx b/src/Components/List/ListItem.tsx index 33e4bc96..418d0b47 100644 --- a/src/Components/List/ListItem.tsx +++ b/src/Components/List/ListItem.tsx @@ -173,8 +173,9 @@ export const ListItem = forwardRef( return cloneElement( linkElement, { + id, style: itemStyle, - title: title, + title, className: listItemClass, 'data-testid': testID, }, From 7e5857c2b14a5ffe4f627893a19f10ed1a37cb59 Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Wed, 22 Jul 2020 09:33:43 -0700 Subject: [PATCH 21/22] Use null instead of undefined and change typing to match --- src/Components/List/List.tsx | 12 ++++++------ src/Components/List/ListItem.tsx | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Components/List/List.tsx b/src/Components/List/List.tsx index 27d8f953..4dbcb757 100644 --- a/src/Components/List/List.tsx +++ b/src/Components/List/List.tsx @@ -25,17 +25,17 @@ import {StandardFunctionProps, InfluxColors, Gradients} from '../../Types' import './List.scss' export interface ListContextProps { - listBackgroundColor?: InfluxColors | string - listGradient?: Gradients - listContrastColor?: InfluxColors | string + listBackgroundColor?: InfluxColors | string | null + listGradient?: Gradients | null + listContrastColor?: InfluxColors | string | null } export type ListItemRef = HTMLDivElement export const ListContext = createContext({ - listBackgroundColor: undefined, - listGradient: undefined, - listContrastColor: undefined, + listBackgroundColor: null, + listGradient: null, + listContrastColor: null, }) export interface ListProps extends StandardFunctionProps { diff --git a/src/Components/List/ListItem.tsx b/src/Components/List/ListItem.tsx index 418d0b47..2d81e5ee 100644 --- a/src/Components/List/ListItem.tsx +++ b/src/Components/List/ListItem.tsx @@ -33,9 +33,9 @@ export interface ListItemSharedProps { } export interface ListItemContextProps extends ListItemSharedProps { - listItemContrastColor?: string - listItemBackgroundColor?: InfluxColors | string - listItemGradient?: Gradients + listItemContrastColor?: string | null + listItemBackgroundColor?: InfluxColors | string | null + listItemGradient?: Gradients | null } type CombinedListItemProps = ListItemSharedProps & StandardFunctionProps @@ -64,9 +64,9 @@ export type ListItemRef = HTMLDivElement export const ListItemContext = createContext({ selected: false, size: ComponentSize.Small, - listItemBackgroundColor: undefined, - listItemGradient: undefined, - listItemContrastColor: undefined, + listItemBackgroundColor: null, + listItemGradient: null, + listItemContrastColor: null, }) export const ListItem = forwardRef( From cd3cdc6c818dd86feb22152cd726e19109bfb344 Mon Sep 17 00:00:00 2001 From: alexpaxton Date: Wed, 22 Jul 2020 09:36:22 -0700 Subject: [PATCH 22/22] Revert "Use null instead of undefined and change typing to match" This reverts commit 7e5857c2b14a5ffe4f627893a19f10ed1a37cb59. --- src/Components/List/List.tsx | 12 ++++++------ src/Components/List/ListItem.tsx | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Components/List/List.tsx b/src/Components/List/List.tsx index 4dbcb757..27d8f953 100644 --- a/src/Components/List/List.tsx +++ b/src/Components/List/List.tsx @@ -25,17 +25,17 @@ import {StandardFunctionProps, InfluxColors, Gradients} from '../../Types' import './List.scss' export interface ListContextProps { - listBackgroundColor?: InfluxColors | string | null - listGradient?: Gradients | null - listContrastColor?: InfluxColors | string | null + listBackgroundColor?: InfluxColors | string + listGradient?: Gradients + listContrastColor?: InfluxColors | string } export type ListItemRef = HTMLDivElement export const ListContext = createContext({ - listBackgroundColor: null, - listGradient: null, - listContrastColor: null, + listBackgroundColor: undefined, + listGradient: undefined, + listContrastColor: undefined, }) export interface ListProps extends StandardFunctionProps { diff --git a/src/Components/List/ListItem.tsx b/src/Components/List/ListItem.tsx index 2d81e5ee..418d0b47 100644 --- a/src/Components/List/ListItem.tsx +++ b/src/Components/List/ListItem.tsx @@ -33,9 +33,9 @@ export interface ListItemSharedProps { } export interface ListItemContextProps extends ListItemSharedProps { - listItemContrastColor?: string | null - listItemBackgroundColor?: InfluxColors | string | null - listItemGradient?: Gradients | null + listItemContrastColor?: string + listItemBackgroundColor?: InfluxColors | string + listItemGradient?: Gradients } type CombinedListItemProps = ListItemSharedProps & StandardFunctionProps @@ -64,9 +64,9 @@ export type ListItemRef = HTMLDivElement export const ListItemContext = createContext({ selected: false, size: ComponentSize.Small, - listItemBackgroundColor: null, - listItemGradient: null, - listItemContrastColor: null, + listItemBackgroundColor: undefined, + listItemGradient: undefined, + listItemContrastColor: undefined, }) export const ListItem = forwardRef(