Skip to content

Commit

Permalink
DS fixes and updates (#1275)
Browse files Browse the repository at this point in the history
  • Loading branch information
jossmac authored Aug 27, 2024
1 parent 1e4e080 commit fac7ba4
Show file tree
Hide file tree
Showing 26 changed files with 312 additions and 105 deletions.
21 changes: 21 additions & 0 deletions .changeset/pink-cougars-invent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
'@keystar/ui': patch
---

Misc. fixes and updates.

Fixes:

- Allow "focus" method on `Picker` ref
- Defensive "current" selector on `NavItem` styles
- Fix text truncation on `Picker` selected text
- Clear slots of `Content` children—resolves issue with `Calendar` elements within `Dialog` receiving incorrect props
- Fix issue with `Tray` when "size" provided to `Dialog` component

Updates:

- Support "isPending" prop on `Button`
- Support "low" prominence `Checkbox`
- Emphasise "selected" state on `ActionButton`
- More prominent `ActionBar`
- Increase `TextArea` min-height to 3 lines
17 changes: 6 additions & 11 deletions design-system/pkg/src/action-bar/ActionBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ import localizedMessages from './l10n.json';
import { ActionBarProps } from './types';
import { actionbarClassList } from './class-list';

const styles = {};

function ActionBar<T extends object>(
props: ActionBarProps<T>,
forwardedRef: ForwardedRef<HTMLDivElement>
Expand Down Expand Up @@ -97,6 +95,7 @@ function ActionBarInner<T>(
}
}, [stringFormatter]);

// FIXME: style props are passed to both the root and the bar elements
return (
<FocusScope restoreFocus>
<div
Expand Down Expand Up @@ -129,9 +128,10 @@ function ActionBarInner<T>(
className={classNames(
css({
alignItems: 'center',
backgroundColor: tokenSchema.color.background.surface,
border: `${tokenSchema.size.border.regular} solid ${tokenSchema.color.border.neutral}`,
borderRadius: tokenSchema.size.radius.medium,
backgroundColor: tokenSchema.color.background.canvas,
border: `${tokenSchema.size.border.regular} solid ${tokenSchema.color.border.emphasis}`,
borderRadius: tokenSchema.size.radius.regular,
boxShadow: `0 1px 4px ${tokenSchema.color.shadow.regular}`,
display: 'grid',
gap: tokenSchema.size.space.small,
gridTemplateAreas: '"clear selected . actiongroup"',
Expand All @@ -150,8 +150,7 @@ function ActionBarInner<T>(
transform: 'translateY(0)',
},
}),
actionbarClassList.element('bar'),
styleProps.className
actionbarClassList.element('bar')
)}
>
<ActionGroup
Expand All @@ -162,10 +161,6 @@ function ActionBarInner<T>(
buttonLabelBehavior="collapse"
onAction={onAction}
gridArea="actiongroup"
UNSAFE_className={classNames(
styles,
'react-spectrum-ActionBar-actionGroup'
)}
>
{children}
</ActionGroup>
Expand Down
9 changes: 5 additions & 4 deletions design-system/pkg/src/action-group/ActionGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -329,10 +329,11 @@ function ActionGroup<T extends object>(
'&:not(:last-of-type)': {
marginRight: `calc(${tokenSchema.size.border.regular} * -1)`,
},
'&.is-hovered, &.is-focused, &.is-pressed': {
zIndex: 1,
},
'&.is-selected': {
'&[data-interaction=hover], &[data-focus=visible], &[data-interaction=press]':
{
zIndex: 1,
},
'&[data-selected]': {
zIndex: 2,
},
},
Expand Down
71 changes: 67 additions & 4 deletions design-system/pkg/src/button/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import { useButton } from '@react-aria/button';
import { useLocalizedStringFormatter } from '@react-aria/i18n';
import { useHover } from '@react-aria/interactions';
import { useLink } from '@react-aria/link';
import { filterDOMProps, mergeProps, useObjectRef } from '@react-aria/utils';
import { ForwardedRef, forwardRef, useMemo } from 'react';
import { ForwardedRef, forwardRef, useEffect, useMemo, useState } from 'react';

import { useProviderProps } from '@keystar/ui/core';
import { SlotProvider, useSlotProps } from '@keystar/ui/slots';
import { FocusRing } from '@keystar/ui/style';
import { Text } from '@keystar/ui/typography';
import { isReactText } from '@keystar/ui/utils';

import localizedMessages from './l10n.json';
import {
ButtonElementProps,
ButtonProps,
CommonButtonProps,
LinkElementProps,
} from './types';
import { buttonClassList, useButtonStyles } from './useButtonStyles';
import { ProgressCircle } from '../progress';

/**
* Buttons are pressable elements that are used to trigger actions, their label
Expand Down Expand Up @@ -104,28 +107,88 @@ const BaseButton = forwardRef(function Button(
props: ButtonElementProps,
forwardedRef: ForwardedRef<HTMLButtonElement>
) {
const { children, isDisabled, ...otherProps } = props;
props = disablePendingProps(props);
const { children, isDisabled, isPending, ...otherProps } = props;

const [isProgressVisible, setIsProgressVisible] = useState(false);
const stringFormatter = useLocalizedStringFormatter(localizedMessages);
const domRef = useObjectRef(forwardedRef);
const { buttonProps, isPressed } = useButton(props, domRef);
const { hoverProps, isHovered } = useHover({ isDisabled });
const styleProps = useButtonStyles(props, { isHovered, isPressed });
const styleProps = useButtonStyles(props, {
isHovered,
isPending: isProgressVisible,
isPressed,
});

// wait a second before showing the progress indicator. for actions that
// resolve quickly, this prevents a flash of the pending treatment.
useEffect(() => {
let timeout: ReturnType<typeof setTimeout>;

if (isPending) {
timeout = setTimeout(() => {
setIsProgressVisible(true);
}, 1000);
} else {
setIsProgressVisible(false);
}
return () => {
clearTimeout(timeout);
};
}, [isPending]);

// prevent form submission when while pending
const pendingProps = isPending
? {
onClick: (e: MouseEvent) => e.preventDefault(),
}
: {
onClick: () => {}, // satisfy TS expectations…
};

return (
<button
ref={domRef}
{...styleProps}
{...filterDOMProps(otherProps, { propNames: new Set(['form']) })}
{...mergeProps(buttonProps, hoverProps)}
{...mergeProps(buttonProps, hoverProps, pendingProps)}
aria-disabled={isPending ? 'true' : undefined}
>
{children}
{isProgressVisible && (
<ProgressCircle
aria-atomic="false"
aria-live="assertive"
aria-label={stringFormatter.format('pending')}
isIndeterminate
size="small"
UNSAFE_style={{ position: 'absolute' }}
/>
)}
</button>
);
});

// Utils
// -----------------------------------------------------------------------------

function disablePendingProps(props: ButtonElementProps) {
// disallow interaction while the button is pending
if (props.isPending) {
props = { ...props };
props.onKeyDown = undefined;
props.onKeyUp = undefined;
props.onPress = undefined;
props.onPressChange = undefined;
props.onPressEnd = undefined;
props.onPressStart = undefined;
props.onPressUp = undefined;
}

return props;
}

export const useButtonChildren = (props: CommonButtonProps) => {
const { children } = props;

Expand Down
2 changes: 1 addition & 1 deletion design-system/pkg/src/button/FieldButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export function useFieldButton(
) {
let { isHovered, isPressed } = state;
const styleProps = useActionButtonStyles(props, { isHovered, isPressed });
let slots = useMemo(() => ({ text: { flex: true, truncate: true } }), []);
let slots = useMemo(() => ({ text: { flex: true } }), []);
let children = useActionButtonChildren(props, slots);

return { children, styleProps };
Expand Down
36 changes: 36 additions & 0 deletions design-system/pkg/src/button/l10n.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"ar-AE": { "pending": "قيد الانتظار" },
"bg-BG": { "pending": "недовършено" },
"cs-CZ": { "pending": "čeká na vyřízení" },
"da-DK": { "pending": "afventende" },
"de-DE": { "pending": "Ausstehend" },
"el-GR": { "pending": "σε εκκρεμότητα" },
"en-US": { "pending": "pending" },
"es-ES": { "pending": "pendiente" },
"et-EE": { "pending": "ootel" },
"fi-FI": { "pending": "odottaa" },
"fr-FR": { "pending": "En attente" },
"he-IL": { "pending": "ממתין ל" },
"hr-HR": { "pending": "u tijeku" },
"hu-HU": { "pending": "függőben levő" },
"it-IT": { "pending": "in sospeso" },
"ja-JP": { "pending": "保留" },
"ko-KR": { "pending": "보류 중" },
"lt-LT": { "pending": "laukiama" },
"lv-LV": { "pending": "gaida" },
"nb-NO": { "pending": "avventer" },
"nl-NL": { "pending": "in behandeling" },
"pl-PL": { "pending": "oczekujące" },
"pt-BR": { "pending": "pendente" },
"pt-PT": { "pending": "pendente" },
"ro-RO": { "pending": "în așteptare" },
"ru-RU": { "pending": "в ожидании" },
"sk-SK": { "pending": "čakajúce" },
"sl-SI": { "pending": "v teku" },
"sr-SP": { "pending": "nerešeno" },
"sv-SE": { "pending": "väntande" },
"tr-TR": { "pending": "beklemede" },
"uk-UA": { "pending": "в очікуванні" },
"zh-CN": { "pending": "待处理" },
"zh-TW": { "pending": "待處理" }
}
37 changes: 37 additions & 0 deletions design-system/pkg/src/button/stories/Button.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useState } from 'react';
import { action } from '@keystar/ui-storybook';

import { plusCircleIcon } from '@keystar/ui/icon/icons/plusCircleIcon';
Expand Down Expand Up @@ -149,6 +150,42 @@ Anchor.story = {
name: 'anchor',
};

export const Pending = () => {
return (
<Flex direction="column" gap="regular" alignItems="start">
<SimulatedPendingButton>Default</SimulatedPendingButton>
<SimulatedPendingButton prominence="low">
Low prominence
</SimulatedPendingButton>
<SimulatedPendingButton prominence="high">
High prominence
</SimulatedPendingButton>
<form
onSubmit={e => {
e.preventDefault();
action('submit')(e);
}}
>
<SimulatedPendingButton type="submit">Submit</SimulatedPendingButton>
</form>
</Flex>
);
};

function SimulatedPendingButton(props: any) {
let [isPending, setPending] = useState(false);

let handlePress = (e: any) => {
action('press')(e);
setPending(true);
setTimeout(() => {
setPending(false);
}, 5000);
};

return <Button {...props} isPending={isPending} onPress={handlePress} />;
}

function render(label = 'Default', props: ButtonProps = {}) {
return (
<Flex gap="regular">
Expand Down
6 changes: 5 additions & 1 deletion design-system/pkg/src/button/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,11 @@ export type CommonButtonProps = {
AriaLabelingProps &
BaseStyleProps;

export type ButtonElementProps = CommonButtonProps & AriaProps;
export type ButtonElementProps = CommonButtonProps &
AriaProps & {
/** Disable events and display a progress indicator. */
isPending?: boolean;
};

export type LinkElementProps = CommonButtonProps & AnchorDOMProps;

Expand Down
32 changes: 22 additions & 10 deletions design-system/pkg/src/button/useActionButtonStyles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function useActionButtonStyles(
cursor: 'default',
display: 'inline-flex',
flexShrink: 0,
fontWeight: tokenSchema.typography.fontWeight.medium,
fontWeight: tokenSchema.typography.fontWeight.regular,
height: tokenSchema.size.element.regular,
justifyContent: 'center',
minWidth: tokenSchema.size.element.regular,
Expand Down Expand Up @@ -108,6 +108,7 @@ export function useActionButtonStyles(
'&[data-interaction=hover]': {
backgroundColor: tokenSchema.color.alias.backgroundHovered,
borderColor: tokenSchema.color.alias.borderHovered,
// boxShadow: `${tokenSchema.size.shadow.small} ${tokenSchema.color.shadow.regular}`,
color: tokenSchema.color.alias.foregroundHovered,
},
'&[data-interaction=press]': {
Expand All @@ -118,12 +119,17 @@ export function useActionButtonStyles(

// states
'&[data-selected]': {
backgroundColor: tokenSchema.color.alias.backgroundSelected,
color: tokenSchema.color.foreground.neutralEmphasis,
backgroundColor: tokenSchema.color.foreground.neutralSecondary,
borderColor: tokenSchema.color.foreground.neutralSecondary,
color: tokenSchema.color.foreground.inverse,

'&[data-interaction=hover]': {
backgroundColor:
tokenSchema.color.alias.backgroundSelectedHovered,
backgroundColor: tokenSchema.color.foreground.neutral,
borderColor: tokenSchema.color.foreground.neutral,
},
'&[data-interaction=press]': {
backgroundColor: tokenSchema.color.foreground.neutralEmphasis,
borderColor: tokenSchema.color.foreground.neutralEmphasis,
},
},
'&:disabled, &[aria-disabled=true], &[data-disabled=true]': {
Expand Down Expand Up @@ -179,20 +185,26 @@ export function useActionButtonStyles(
// interactions
'&[data-interaction=hover]': {
backgroundColor: tokenSchema.color.alias.backgroundHovered,
color: tokenSchema.color.foreground.neutralEmphasis,
color: tokenSchema.color.alias.foregroundHovered,
},
'&[data-interaction=press]': {
backgroundColor: tokenSchema.color.alias.backgroundPressed,
color: tokenSchema.color.alias.foregroundPressed,
},

// states
'&[data-selected]': {
backgroundColor: tokenSchema.color.alias.backgroundSelected,
color: tokenSchema.color.alias.foregroundSelected,
backgroundColor: tokenSchema.color.foreground.neutralSecondary,
borderColor: tokenSchema.color.foreground.neutralSecondary,
color: tokenSchema.color.foreground.inverse,

'&[data-interaction=hover]': {
backgroundColor:
tokenSchema.color.alias.backgroundSelectedHovered,
backgroundColor: tokenSchema.color.foreground.neutral,
borderColor: tokenSchema.color.foreground.neutral,
},
'&[data-interaction=press]': {
backgroundColor: tokenSchema.color.foreground.neutralEmphasis,
borderColor: tokenSchema.color.foreground.neutralEmphasis,
},
},
'&:disabled, &[aria-disabled=true], &[data-disabled=true]': {
Expand Down
Loading

0 comments on commit fac7ba4

Please sign in to comment.