Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(InfoSheet): update styles and allow adding a button to the content #1294

Merged
merged 9 commits into from
Nov 25, 2024
4 changes: 4 additions & 0 deletions src/__private_stories__/sheet-presets-story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -215,13 +215,15 @@ ActionsList.argTypes = {

type InfoSheetArgs = SheetArgs & {
numItems: number;
buttonText: string;
iconType: 'bullet' | 'regular' | 'small';
};

export const Info: StoryComponent<InfoSheetArgs> = ({
title,
subtitle,
description,
buttonText,
multiparagraphDescription,
numItems,
iconType,
Expand All @@ -246,6 +248,7 @@ export const Info: StoryComponent<InfoSheetArgs> = ({
onClose={() => {
setOpen(false);
}}
button={buttonText ? {text: buttonText} : undefined}
title={title}
subtitle={subtitle}
description={
Expand Down Expand Up @@ -276,6 +279,7 @@ Info.storyName = 'InfoSheet';
Info.args = {
title: 'Title',
subtitle: 'Subtitle',
buttonText: '',
description: 'Description',
numItems: 5,
iconType: 'bullet',
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions src/__screenshot_tests__/sheet-screenshot-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,23 @@ test('InfoSheet with multiple description paragraphs', async () => {
expect(image).toMatchImageSnapshot();
});

test.each(TESTABLE_DEVICES)('InfoSheet with dismiss button in %s', async (device) => {
const page = await openStoryPage({
id: 'private-sheet-presets--info',
device,
args: {buttonText: 'Dismiss'},
});

const button = await screen.findByRole('button', {name: 'Open'});
await button.click();

await screen.findByRole('dialog');

const image = await page.screenshot();

expect(image).toMatchImageSnapshot();
});

test.each(TESTABLE_DEVICES_WITH_LARGE_DESKTOP)('ActionsSheet in %s', async (device) => {
const page = await openStoryPage({
id: 'private-sheet-presets--actions',
Expand Down
1 change: 1 addition & 0 deletions src/__stories__/sheet-story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export const ShowSheet: StoryComponent<ShowSheetArgs> = ({
},
},
],
button: {text: 'Dismiss'},
},
}).then((response) => {
setOpenDialogType(undefined);
Expand Down
12 changes: 12 additions & 0 deletions src/sheet-info.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,15 @@ export const infoItemIcon = sprinkles({
alignItems: 'center',
height: 24,
});

export const infoItemIconContainer = sprinkles({
height: '100%',
display: 'flex',
});

export const itemContainer = sprinkles({
display: 'flex',
alignItems: 'center',
minHeight: 72,
paddingY: 16,
});
75 changes: 48 additions & 27 deletions src/sheet-info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {Text3, Text2} from './text';
import {vars as skinVars} from './skins/skin-contract.css';
import * as styles from './sheet-info.css';
import Image from './image';
import {ButtonPrimary} from './button';

import type {ExclusifyUnion} from './utils/utility-types';
import type {DataAttributes, IconProps} from './utils/types';
Expand Down Expand Up @@ -38,47 +39,67 @@ type InfoSheetProps = {
}>;
onClose?: () => void;
dataAttributes?: DataAttributes;
button?: {
text: string;
};
};

const InfoSheet = React.forwardRef<HTMLDivElement, InfoSheetProps>(
({title, subtitle, description, items, onClose, dataAttributes}, ref) => {
({title, subtitle, description, items, onClose, button, dataAttributes}, ref) => {
const {isDarkMode} = useTheme();
return (
<Sheet
onClose={onClose}
ref={ref}
dataAttributes={{'component-name': 'InfoSheet', ...dataAttributes}}
>
{({modalTitleId}) => (
{({closeModal, modalTitleId}) => (
<SheetBody
title={title}
subtitle={subtitle}
description={description}
modalTitleId={modalTitleId}
button={
button ? (
<ButtonPrimary onPress={closeModal}>{button.text}</ButtonPrimary>
) : undefined
}
>
<Box paddingBottom={16}>
<Stack space={16} role="list">
{items.map((item, idx) => (
<Inline key={item.id || idx} space={8}>
<div className={styles.infoItemIcon}>
{item.icon.type === 'bullet' ? (
<Circle
size={8}
backgroundColor={skinVars.colors.textPrimary}
/>
) : item.icon.Icon ? (
<item.icon.Icon size={item.icon.type === 'small' ? 16 : 24} />
) : (
<Image
src={
isDarkMode && item.icon.urlDark
? item.icon.urlDark
: item.icon.url
}
width={item.icon.type === 'small' ? 16 : 24}
height={item.icon.type === 'small' ? 16 : 24}
/>
)}
<Box paddingBottom={16} role="list">
{items.map((item, idx) => (
<div key={item.id || idx} className={styles.itemContainer} role="listitem">
<Inline space={8}>
<div
className={styles.infoItemIconContainer}
style={{
alignItems:
item.icon.type !== 'bullet' && !item.description
? 'center'
: undefined,
}}
>
<div className={styles.infoItemIcon}>
{item.icon.type === 'bullet' ? (
<Circle
size={8}
backgroundColor={skinVars.colors.textPrimary}
/>
) : item.icon.Icon ? (
<item.icon.Icon
size={item.icon.type === 'small' ? 16 : 24}
/>
) : (
<Image
src={
isDarkMode && item.icon.urlDark
? item.icon.urlDark
: item.icon.url
}
width={item.icon.type === 'small' ? 16 : 24}
height={item.icon.type === 'small' ? 16 : 24}
/>
)}
</div>
</div>
<Stack space={2}>
<Text3 regular>{item.title}</Text3>
Expand All @@ -89,8 +110,8 @@ const InfoSheet = React.forwardRef<HTMLDivElement, InfoSheetProps>(
)}
</Stack>
</Inline>
))}
</Stack>
</div>
))}
</Box>
</SheetBody>
)}
Expand Down
39 changes: 25 additions & 14 deletions src/sheet-native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,25 +88,36 @@ const showActionsListNativeSheet = (

const showInfoNativeSheet = async (
nativeSheetImplementation: NativeSheetImplementation,
{title, subtitle, description, items}: SheetPropsByType['INFO']
{title, subtitle, description, items, button}: SheetPropsByType['INFO']
): Promise<SheetResultByType['INFO']> => {
// nothing to return, this is an informative sheet
await (nativeSheetImplementation as NativeSheetImplementation)({
const infoSheetContent = {
type: 'LIST' as const,
id: 'list-0',
listType: 'INFORMATIVE' as const,
autoSubmit: false,
selectedIds: [],
items,
};

return await (nativeSheetImplementation as NativeSheetImplementation)({
title,
subtitle,
// TODO: add multiline support to native sheet
description: normalizeDescriptionForNative(description),
content: [
{
type: 'LIST',
id: 'list-0',
listType: 'INFORMATIVE',
autoSubmit: false,
selectedIds: [],
items,
},
],
});
content: button
atabel marked this conversation as resolved.
Show resolved Hide resolved
? [
infoSheetContent,
{
type: 'BOTTOM_ACTIONS',
id: 'bottom-actions-0',
button,
},
]
: [infoSheetContent],
}).then(() => ({
// this is an informative sheet, it can only be dismissed
action: 'DISMISS',
}));
};

const showActionsNativeSheet = (
Expand Down
5 changes: 4 additions & 1 deletion src/sheet-types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ export type SheetPropsByType = {
description?: string;
icon: InfoIcon;
}>;
button?: {
text: string;
};
}>;
ACTIONS: SheetProps<{
button: {
Expand All @@ -71,7 +74,7 @@ export type SheetType = keyof SheetPropsByType;
export type SheetResultByType = {
RADIO_LIST: {action: 'SUBMIT'; selectedId: string} | {action: 'DISMISS'};
ACTIONS_LIST: {action: 'SUBMIT'; selectedId: string} | {action: 'DISMISS'};
INFO: void;
INFO: {action: 'DISMISS'};
Copy link
Contributor Author

@marcoskolodny marcoskolodny Nov 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's reasonable to always return 'DISMISS' instead of nothing. If an user doesn't want to do anything with it, they can just ignore the result

ACTIONS: {action: 'PRIMARY' | 'SECONDARY' | 'LINK' | 'DISMISS'};
};

Expand Down
2 changes: 1 addition & 1 deletion src/sheet-web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const SheetWeb = ({sheetProps, onResolve}: SheetWebProps): JSX.Element => {
}
switch (sheetProps.type) {
case 'INFO':
onResolve<'INFO'>(undefined);
onResolve<'INFO'>({action: 'DISMISS'});
break;
case 'ACTIONS_LIST':
if (selectionRef.current) {
Expand Down
Loading