Skip to content

Commit

Permalink
feat(components): add Toolbar and Calendar presets (#1326)
Browse files Browse the repository at this point in the history
* feat(components): add `Toolbar` and `Calendar` presets

* chore: format

* fix: biome

* feat: add spacing variants

* fix: version
  • Loading branch information
Niznikr authored Aug 29, 2024
1 parent 748270f commit c9c1800
Show file tree
Hide file tree
Showing 22 changed files with 516 additions and 74 deletions.
5 changes: 5 additions & 0 deletions .changeset/beige-zebras-occur.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@launchpad-ui/components": patch
---

Add `Toolbar` and `Calendar` presets
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"homepage": "https://github.com/launchdarkly/launchpad-ui#readme",
"devDependencies": {
"@axe-core/playwright": "^4.10.0",
"@biomejs/biome": "1.8.1",
"@biomejs/biome": "1.8.3",
"@changesets/changelog-github": "^0.5.0",
"@changesets/cli": "^2.27.1",
"@commitlint/cli": "^19.4.0",
Expand Down
24 changes: 24 additions & 0 deletions packages/components/__tests__/Toolbar.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { describe, expect, it } from 'vitest';

import { render, screen } from '../../../test/utils';
import { Button, Group, IconButton, Separator, Toolbar } from '../src';

describe('Toolbar', () => {
it('renders', () => {
render(
<Toolbar>
<Group>
<Button size="small">Cut</Button>
<Button size="small">Copy</Button>
<Button size="small">Paste</Button>
</Group>
<Separator orientation="vertical" />
<Group>
<IconButton icon="gear" aria-label="settings" size="small" />
<IconButton icon="help" aria-label="help" size="small" />
</Group>
</Toolbar>,
);
expect(screen.getByRole('toolbar')).toBeVisible();
});
});
2 changes: 1 addition & 1 deletion packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"test": "vitest run --coverage"
},
"dependencies": {
"@internationalized/date": "3.5.5",
"@launchpad-ui/icons": "workspace:~",
"@launchpad-ui/tokens": "workspace:~",
"@react-aria/toast": "3.0.0-beta.15",
Expand All @@ -51,7 +52,6 @@
"react-dom": "18.3.1"
},
"devDependencies": {
"@internationalized/date": "3.5.5",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-stately": "3.32.2"
Expand Down
69 changes: 66 additions & 3 deletions packages/components/src/Calendar.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,46 @@
import type { ForwardedRef } from 'react';
import type { CalendarDate } from '@internationalized/date';
import type { RangeValue } from '@react-types/shared';
import type { ForwardedRef, HTMLAttributes } from 'react';
import type {
CalendarCellProps,
CalendarGridBodyProps,
CalendarGridHeaderProps,
CalendarGridProps,
CalendarHeaderCellProps,
CalendarProps,
DateRange,
DateValue,
RangeCalendarProps,
} from 'react-aria-components';
import type { ButtonProps } from './Button';

import { cva, cx } from 'class-variance-authority';
import { forwardRef } from 'react';
import { forwardRef, useState } from 'react';
import {
Calendar as AriaCalendar,
CalendarCell as AriaCalendarCell,
RangeCalendar as AriaRangeCalendar,
ButtonContext,
CalendarContext,
CalendarGrid,
CalendarGridBody,
CalendarGridHeader,
CalendarHeaderCell,
Provider,
RangeCalendarContext,
composeRenderProps,
useSlottedContext,
} from 'react-aria-components';

import { button } from './Button';
import { Button, button } from './Button';
import styles from './styles/Calendar.module.css';

interface CalendarPickerProps extends HTMLAttributes<HTMLDivElement> {}

interface PresetProps extends Omit<ButtonProps, 'value'> {
value: CalendarDate | RangeValue<CalendarDate>;
}

const calendar = cva(styles.calendar);
const cell = cva(styles.cell);
const range = cva(styles.range);
Expand Down Expand Up @@ -93,13 +108,59 @@ const _RangeCalendar = <T extends DateValue>(
*/
const RangeCalendar = forwardRef(_RangeCalendar);

const _CalendarPicker = (
{ children, className, ...props }: CalendarPickerProps,
ref: ForwardedRef<HTMLDivElement>,
) => {
const [value, onChange] = useState<DateValue>();
const [range, onChangeRange] = useState<DateRange | null>();
const [focusedValue, onFocusChange] = useState<DateValue>();
return (
<Provider
values={[
[CalendarContext, { value, onChange, focusedValue, onFocusChange }],
[
RangeCalendarContext,
{ value: range, onChange: onChangeRange, focusedValue, onFocusChange },
],
[ButtonContext, {}],
]}
>
<div {...props} ref={ref} className={cx(styles.picker, className)}>
{children}
</div>
</Provider>
);
};

const CalendarPicker = forwardRef(_CalendarPicker);

const _Preset = ({ value, ...props }: PresetProps, ref: ForwardedRef<HTMLButtonElement>) => {
const context = useSlottedContext(CalendarContext);
const rangeContext = useSlottedContext(RangeCalendarContext);
const onPress = () => {
if ('start' in value) {
rangeContext?.onFocusChange?.(value.start);
rangeContext?.onChange?.(value);
} else {
context?.onFocusChange?.(value);
context?.onChange?.(value);
}
};
return <Button ref={ref} size="small" variant="minimal" {...props} onPress={onPress} />;
};

const Preset = forwardRef(_Preset);

export {
Calendar,
CalendarCell,
CalendarGrid,
CalendarGridBody,
CalendarGridHeader,
CalendarHeaderCell,
CalendarPicker,
Preset,
RangeCalendar,
};
export type {
Expand All @@ -109,5 +170,7 @@ export type {
CalendarGridBodyProps,
CalendarGridHeaderProps,
CalendarHeaderCellProps,
CalendarPickerProps,
PresetProps,
RangeCalendarProps,
};
49 changes: 49 additions & 0 deletions packages/components/src/Toolbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { VariantProps } from 'class-variance-authority';
import type { ForwardedRef } from 'react';
import type { ToolbarProps as AriaToolbarProps } from 'react-aria-components';

import { cva } from 'class-variance-authority';
import { forwardRef } from 'react';
import { Toolbar as AriaToolbar, composeRenderProps } from 'react-aria-components';

import styles from './styles/Toolbar.module.css';

const toolbar = cva(styles.base, {
variants: {
spacing: {
basic: styles.basic,
compact: styles.compact,
large: styles.large,
},
},
defaultVariants: {
spacing: 'basic',
},
});

interface ToolbarProps extends AriaToolbarProps, VariantProps<typeof toolbar> {}

const _Toolbar = (
{ spacing = 'basic', ...props }: ToolbarProps,
ref: ForwardedRef<HTMLDivElement>,
) => {
return (
<AriaToolbar
{...props}
ref={ref}
className={composeRenderProps(props.className, (className, renderProps) =>
toolbar({ ...renderProps, spacing, className }),
)}
/>
);
};

/**
* A toolbar is a container for a set of interactive controls, such as buttons, dropdown menus, or checkboxes, with arrow key navigation.
*
* https://react-spectrum.adobe.com/react-aria/Toolbar.html
*/
const Toolbar = forwardRef(_Toolbar);

export { Toolbar };
export type { ToolbarProps };
6 changes: 6 additions & 0 deletions packages/components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export type {
CalendarGridBodyProps,
CalendarGridHeaderProps,
CalendarHeaderCellProps,
CalendarPickerProps,
PresetProps,
RangeCalendarProps,
} from './Calendar';
export type { CheckboxProps } from './Checkbox';
Expand Down Expand Up @@ -66,6 +68,7 @@ export type { TextFieldProps } from './TextField';
export type { SnackbarValue, ToastOptions, ToastValue } from './Toast';
export type { ToggleButtonProps } from './ToggleButton';
export type { ToggleIconButtonProps } from './ToggleIconButton';
export type { ToolbarProps } from './Toolbar';
export type { TooltipProps, TooltipTriggerProps } from './Tooltip';

export { Alert } from './Alert';
Expand All @@ -79,6 +82,8 @@ export {
CalendarGridBody,
CalendarGridHeader,
CalendarHeaderCell,
CalendarPicker,
Preset,
RangeCalendar,
} from './Calendar';
export { Checkbox } from './Checkbox';
Expand Down Expand Up @@ -138,5 +143,6 @@ export { TextField } from './TextField';
export { SnackbarContainer, SnackbarQueue, ToastContainer, ToastQueue } from './Toast';
export { ToggleButton } from './ToggleButton';
export { ToggleIconButton } from './ToggleIconButton';
export { Toolbar } from './Toolbar';
export { Tooltip, TooltipTrigger } from './Tooltip';
export { useHref, useMedia } from './utils';
2 changes: 1 addition & 1 deletion packages/components/src/styles/Alert.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
background-color: var(--lp-color-bg-feedback-warning);
}

&:has( .heading) {
&:has(.heading) {
& .icon {
transform: translateY(2px);
}
Expand Down
13 changes: 13 additions & 0 deletions packages/components/src/styles/Calendar.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,16 @@
}
}
}

.picker {
display: flex;
gap: var(--lp-spacing-300);

&:has([data-orientation='horizontal']) {
flex-direction: column;
}

&:has([data-orientation='vertical']) {
flex-direction: row;
}
}
4 changes: 2 additions & 2 deletions packages/components/src/styles/Group.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
overflow: hidden;
width: 100%;

&:has( [slot='increment'], [slot='decrement']) {
&:has([slot='increment'], [slot='decrement']) {
gap: 0;
}

&:has( button[data-rac]) {
&:has(button[data-rac]) {
padding-block: 3px;
}

Expand Down
6 changes: 3 additions & 3 deletions packages/components/src/styles/Menu.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
min-width: var(--lp-size-144);
max-width: var(--lp-menu-max-width);

&:not(:has( [role='group'])) {
&:not(:has([role='group'])) {
padding: var(--lp-spacing-200);
}
}
Expand Down Expand Up @@ -53,15 +53,15 @@
flex: 1;
}

&:has( [slot='label']) {
&:has([slot='label']) {
& .content {
display: grid;
grid-template-areas:
'label'
'desc';
}

&:has( kbd) {
&:has(kbd) {
& .content {
grid-template-areas:
'label kbd'
Expand Down
10 changes: 5 additions & 5 deletions packages/components/src/styles/Modal.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
flex-grow: 1;
}

& [role='dialog']:has( [slot='body']) {
& [role='dialog']:has([slot='body']) {
display: flex;
flex-direction: column;
gap: var(--lp-spacing-600);
Expand Down Expand Up @@ -146,11 +146,11 @@
&[data-entering] {
animation-name: fade;

&:has( .default) {
&:has(.default) {
animation-duration: var(--lp-duration-200);
}

&:has( .drawer) {
&:has(.drawer) {
animation-duration: var(--lp-duration-300);
}
}
Expand All @@ -160,11 +160,11 @@
animation-direction: reverse;
animation-timing-function: ease-in;

&:has( .default) {
&:has(.default) {
animation-duration: var(--lp-duration-200);
}

&:has( .drawer) {
&:has(.drawer) {
animation-duration: var(--lp-duration-300);
}
}
Expand Down
Loading

0 comments on commit c9c1800

Please sign in to comment.