Skip to content

Commit

Permalink
fix: Pricing Calculator crash + Typescript fixes (#544)
Browse files Browse the repository at this point in the history
  • Loading branch information
dogmar authored Nov 29, 2023
1 parent 257aef2 commit 2228bb4
Show file tree
Hide file tree
Showing 15 changed files with 203 additions and 101 deletions.
18 changes: 8 additions & 10 deletions src/components/Chip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,15 @@ import {
} from 'react'
import styled, { type DefaultTheme, useTheme } from 'styled-components'

import { SEVERITIES } from '../types'

import { Spinner } from './Spinner'
import Card, { type BaseCardProps } from './Card'
import { type FillLevel, useFillLevel } from './contexts/FillLevelContext'
import CloseIcon from './icons/CloseIcon'

const HUES = ['default', 'lighter', 'lightest'] as const
const SIZES = ['small', 'medium', 'large'] as const
const SEVERITIES = [
'neutral',
'info',
'success',
'warning',
'error',
'critical',
] as const

type ChipHue = (typeof HUES)[number]
type ChipSize = (typeof SIZES)[number]
Expand Down Expand Up @@ -65,17 +59,21 @@ const severityToColor = {
info: 'text-primary-accent',
success: 'text-success-light',
warning: 'text-warning-light',
error: 'text-danger-light',
danger: 'text-danger-light',
critical: 'text-danger',
// deprecated
error: 'text-danger-light',
} as const satisfies Record<ChipSeverity, string>

const severityToIconColor = {
neutral: 'icon-default',
info: 'icon-info',
success: 'icon-success',
warning: 'icon-warning',
error: 'icon-danger',
danger: 'icon-danger',
critical: 'icon-danger-critical',
// deprecated
error: 'icon-danger',
} as const satisfies Record<ChipSeverity, keyof DefaultTheme['colors']>

const sizeToCloseHeight = {
Expand Down
40 changes: 36 additions & 4 deletions src/components/ListBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,20 @@ const ScrollContainer = styled.div<ScrollContainerProps>(
})
)

function propsToTextValue({
textValue,
children,
label,
}: Record<string, unknown>) {
return typeof textValue === 'string' && textValue
? textValue
: typeof label === 'string' && label
? label
: typeof children === 'string' && children
? children
: ''
}

function useItemWrappedChildren(
children: ReactElement | ReactElement[],
header?: ReactElement,
Expand All @@ -100,16 +114,25 @@ function useItemWrappedChildren(
const wrapped: JSX.Element[] = []

if (header) {
wrapped.push(<Item key={HEADER_KEY}>{header}</Item>)
const { textValue: _, ...headerProps } = header.props

wrapped.push(
<Item
textValue={propsToTextValue(header.props)}
key={HEADER_KEY}
>
{cloneElement(header, headerProps)}
</Item>
)
}
Children.forEach(children, (child) => {
const { textValue, ...childProps } = child?.props || {}
const { textValue: _, ...childProps } = child.props

if (child) {
const item = (
<Item
key={child.key}
textValue={textValue || ''}
textValue={propsToTextValue(child.props)}
>
{cloneElement(child, childProps)}
</Item>
Expand All @@ -120,7 +143,16 @@ function useItemWrappedChildren(
})

if (footer) {
wrapped.push(<Item key={FOOTER_KEY}>{footer}</Item>)
const { textValue: _, ...footerProps } = footer.props

wrapped.push(
<Item
textValue={propsToTextValue(footer.props)}
key={FOOTER_KEY}
>
{cloneElement(footer, footerProps)}
</Item>
)
}

return wrapped
Expand Down
53 changes: 7 additions & 46 deletions src/components/SelectItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import {
cloneElement,
forwardRef,
useContext,
useEffect,
useId,
useRef,
useState,
} from 'react'
import styled from 'styled-components'

Expand Down Expand Up @@ -48,50 +45,19 @@ const SelectItemWrap = styled.label<SelectItemWrapProps>(
type SelectItemProps = AriaRadioProps & {
icon: ReactElement
label?: string
selected?: boolean
defaultSelected?: boolean
checked?: boolean
name?: string
onChange?: (e: { target: { checked: boolean } }) => void
className?: string
}

const SelectItem = forwardRef<HTMLDivElement, SelectItemProps>(
({
icon,
label,
value,
checked: checkedProp,
defaultSelected,
'aria-describedby': ariaDescribedBy,
onChange,
onBlur,
onFocus,
onKeyDown,
onKeyUp,
name,
}) => {
const [checked, setChecked] = useState(defaultSelected || checkedProp)
const state = useContext(RadioContext) || {
setSelectedValue: () => {},
selectedValue: checkedProp || checked ? value : undefined,
}

useEffect(() => {
setChecked(checkedProp)
}, [checkedProp])

const labelId = useId()
const SelectItem = forwardRef<any, SelectItemProps>(
({ icon, label, value, name, className, ...props }, ref) => {
const state = useContext(RadioContext)
const inputRef = useRef<any>()
const { isFocusVisible, focusProps } = useFocusRing()
const { inputProps, isSelected } = useRadio(
{
value,
'aria-describedby': ariaDescribedBy,
'aria-labelledby': labelId,
onBlur,
onFocus,
onKeyDown,
onKeyUp,
...props,
},
state,
inputRef
Expand All @@ -103,6 +69,8 @@ const SelectItem = forwardRef<HTMLDivElement, SelectItemProps>(
<SelectItemWrap
selected={isSelected}
focused={isFocusVisible}
className={className}
ref={ref}
>
{icon}
{label && <div className="label">{label}</div>}
Expand All @@ -111,13 +79,6 @@ const SelectItem = forwardRef<HTMLDivElement, SelectItemProps>(
{...inputProps}
{...focusProps}
name={inputProps.name || name}
onChange={(e) => {
if (typeof onChange === 'function') {
onChange(e)
}
setChecked(!checked)
inputProps.onChange(e)
}}
ref={inputRef}
/>
</VisuallyHidden>
Expand Down
58 changes: 55 additions & 3 deletions src/components/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import { useVirtualizer } from '@tanstack/react-virtual'
import styled, { useTheme } from 'styled-components'
import { isEmpty, isNil } from 'lodash-es'

import usePrevious from '../hooks/usePrevious'

import Button from './Button'
import CaretUpIcon from './icons/CaretUpIcon'
import ArrowRightIcon from './icons/ArrowRightIcon'
Expand All @@ -52,9 +54,15 @@ export type TableProps = Omit<
| 'scrollTopMargin'
| 'virtualizeRows'
| 'virtualizerOptions'
| 'lockColumnsOnFirstScroll'
| 'reactVirtualOptions'
| 'reactTableOptions'
| 'onRowClick'
| 'emptyStateProps'
| 'hasNextPage'
| 'fetchNextPage'
| 'isFetchingNextPage'
| 'onVirtualWindowChange'
> & {
data: any[]
columns: any[]
Expand All @@ -65,16 +73,19 @@ export type TableProps = Omit<
scrollTopMargin?: number
virtualizeRows?: boolean
lockColumnsOnFirstScroll?: boolean
reactVirtualOptions?: Omit<
Parameters<typeof useVirtualizer>[0],
'count' | 'getScrollElement'
reactVirtualOptions?: Partial<
Omit<Parameters<typeof useVirtualizer>[0], 'count' | 'getScrollElement'>
>
reactTableOptions?: Partial<Omit<TableOptions<any>, 'data' | 'columns'>>
onRowClick?: (e: MouseEvent<HTMLTableRowElement>, row: Row<any>) => void
emptyStateProps?: ComponentProps<typeof EmptyState>
hasNextPage?: boolean
fetchNextPage?: () => void
isFetchingNextPage?: boolean
onVirtualSliceChange?: (slice: {
start: VirtualItem
end: VirtualItem
}) => void
}

const propTypes = {}
Expand Down Expand Up @@ -458,6 +469,40 @@ function useOnFirstScroll(
}, [hasScrolled, onFirstScroll, ref])
}

function useOnVirtualSliceChange({
virtualRows,
virtualizeRows,
onVirtualSliceChange,
}: {
virtualRows: VirtualItem[]
virtualizeRows: boolean
onVirtualSliceChange: (slice: {
start: VirtualItem
end: VirtualItem
}) => void
}) {
const sliceStartRow = virtualRows[0]
const sliceEndRow = virtualRows[virtualRows.length - 1]
const prevSliceStartRow = usePrevious(virtualRows[0])
const prevSliceEndRow = usePrevious(virtualRows[virtualRows.length - 1])

useEffect(() => {
if (
virtualizeRows &&
(prevSliceEndRow !== sliceEndRow || prevSliceStartRow !== sliceStartRow)
) {
onVirtualSliceChange?.({ start: sliceStartRow, end: sliceEndRow })
}
}, [
sliceStartRow,
sliceEndRow,
virtualizeRows,
onVirtualSliceChange,
prevSliceEndRow,
prevSliceStartRow,
])
}

function TableRef(
{
data,
Expand All @@ -477,6 +522,7 @@ function TableRef(
hasNextPage,
isFetchingNextPage,
fetchNextPage,
onVirtualSliceChange,
...props
}: TableProps,
forwardRef: Ref<any>
Expand Down Expand Up @@ -515,9 +561,13 @@ function TableRef(
>(null)

const { rows: tableRows } = table.getRowModel()
const getItemKey = useCallback<
Parameters<typeof useVirtualizer>[0]['getItemKey']
>((i) => tableRows[i]?.id || i, [tableRows])
const rowVirtualizer = useVirtualizer({
count: hasNextPage ? tableRows.length + 1 : tableRows.length,
overscan: 6,
getItemKey,
getScrollElement: () => tableContainerRef.current,
estimateSize: () => 52,
measureElement: (el) => {
Expand All @@ -539,6 +589,8 @@ function TableRef(
const virtualRows = rowVirtualizer.getVirtualItems()
const virtualHeight = rowVirtualizer.getTotalSize()

useOnVirtualSliceChange({ virtualRows, virtualizeRows, onVirtualSliceChange })

lockColumnsOnFirstScroll = lockColumnsOnFirstScroll ?? virtualizeRows
useOnFirstScroll(
tableContainerRef,
Expand Down
14 changes: 10 additions & 4 deletions src/components/Toast.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
import { type FlexProps } from 'honorable'
import { type Ref, forwardRef, useCallback, useEffect, useState } from 'react'

import { type Severity } from '../types'
import { type Extends } from '../utils/ts-utils'

import Banner from './Banner'
import Layer, { type LayerPositionType } from './Layer'

export type Severity = 'info' | 'success' | 'error'
export type ToastSeverity = Extends<
Severity,
'info' | 'success' | 'danger' | 'error'
>

type ToastProps = {
position?: LayerPositionType
closeTimeout?: number | 'none' | 'default'
onClose?: () => void
onCloseComplete?: () => void
show?: boolean
severity?: Severity
severity?: ToastSeverity
} & FlexProps

const defaults = {
closeTimeout: 10000, // 10 seconds
position: 'bottom-right' as LayerPositionType,
onClose: () => {},
onCloseComplete: () => {},
severity: 'info' as Severity,
severity: 'info' as ToastSeverity,
}

const Toast = forwardRef(
Expand Down Expand Up @@ -99,7 +105,7 @@ function GraphQLToast({
}: GraphQLToastProps): JSX.Element {
return (
<Toast
severity="error"
severity="danger"
{...props}
>
{header}: {error?.graphQLErrors[0]?.message}
Expand Down
Loading

0 comments on commit 2228bb4

Please sign in to comment.