Skip to content

Commit 249096e

Browse files
authored
Merge pull request #32339 from software-mansion-labs/wave8/create-a-workspace-card
[Wave8] Create a Workspace Card
2 parents 9ea6b39 + 0ea0c16 commit 249096e

File tree

10 files changed

+127
-30
lines changed

10 files changed

+127
-30
lines changed

assets/images/simple-illustrations/simple-illustration__hotdogstand.svg

Lines changed: 1 addition & 0 deletions
Loading

src/components/DatePicker/CalendarPicker/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ class CalendarPicker extends React.PureComponent {
236236
<View
237237
style={[
238238
this.props.themeStyles.calendarDayContainer,
239-
isSelected ? this.props.themeStyles.calendarDayContainerSelected : {},
239+
isSelected ? this.props.themeStyles.buttonDefaultBG : {},
240240
!isDisabled ? this.props.StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed)) : {},
241241
]}
242242
>

src/components/Icon/Illustrations.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import ConciergeNew from '@assets/images/simple-illustrations/simple-illustratio
3535
import CreditCardsNew from '@assets/images/simple-illustrations/simple-illustration__credit-cards.svg';
3636
import EmailAddress from '@assets/images/simple-illustrations/simple-illustration__email-address.svg';
3737
import HandEarth from '@assets/images/simple-illustrations/simple-illustration__handearth.svg';
38+
import HotDogStand from '@assets/images/simple-illustrations/simple-illustration__hotdogstand.svg';
3839
import InvoiceBlue from '@assets/images/simple-illustrations/simple-illustration__invoice.svg';
3940
import LockOpen from '@assets/images/simple-illustrations/simple-illustration__lockopen.svg';
4041
import Luggage from '@assets/images/simple-illustrations/simple-illustration__luggage.svg';
@@ -60,6 +61,7 @@ export {
6061
ConciergeExclamation,
6162
CreditCardsBlue,
6263
EmailAddress,
64+
HotDogStand,
6365
InvoiceOrange,
6466
JewelBoxBlue,
6567
JewelBoxGreen,

src/components/MenuItem.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ function MenuItem(
531531
{badgeText && (
532532
<Badge
533533
text={badgeText}
534-
badgeStyles={[styles.alignSelfCenter, brickRoadIndicator ? styles.mr2 : undefined, focused || isHovered || pressed ? styles.hoveredButton : {}]}
534+
badgeStyles={[styles.alignSelfCenter, brickRoadIndicator ? styles.mr2 : undefined, focused || isHovered || pressed ? styles.buttonHoveredBG : {}]}
535535
/>
536536
)}
537537
{/* Since subtitle can be of type number, we should allow 0 to be shown */}

src/components/Section/IconSection.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import PropTypes from 'prop-types';
2+
import React from 'react';
3+
import {View} from 'react-native';
4+
import Icon from '@components/Icon';
5+
import useThemeStyles from '@hooks/useThemeStyles';
6+
7+
const iconSectionPropTypes = {
8+
icon: PropTypes.icon,
9+
IconComponent: PropTypes.IconComponent,
10+
iconContainerStyles: PropTypes.iconContainerStyles,
11+
};
12+
13+
const defaultIconSectionPropTypes = {
14+
icon: null,
15+
IconComponent: null,
16+
iconContainerStyles: [],
17+
};
18+
19+
function IconSection({icon, IconComponent, iconContainerStyles}) {
20+
const styles = useThemeStyles();
21+
22+
return (
23+
<View style={[styles.flexGrow1, styles.flexRow, styles.justifyContentEnd, ...iconContainerStyles]}>
24+
{Boolean(icon) && (
25+
<Icon
26+
src={icon}
27+
height={68}
28+
width={68}
29+
/>
30+
)}
31+
{Boolean(IconComponent) && <IconComponent />}
32+
</View>
33+
);
34+
}
35+
36+
IconSection.displayName = 'IconSection';
37+
IconSection.propTypes = iconSectionPropTypes;
38+
IconSection.defaultProps = defaultIconSectionPropTypes;
39+
40+
export default IconSection;

src/components/Section.js renamed to src/components/Section/index.js

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
import PropTypes from 'prop-types';
22
import React from 'react';
33
import {View} from 'react-native';
4+
import MenuItemList from '@components/MenuItemList';
5+
import menuItemPropTypes from '@components/menuItemPropTypes';
6+
import Text from '@components/Text';
47
import useThemeStyles from '@hooks/useThemeStyles';
5-
import Icon from './Icon';
6-
import MenuItemList from './MenuItemList';
7-
import menuItemPropTypes from './menuItemPropTypes';
8-
import Text from './Text';
8+
import IconSection from './IconSection';
9+
10+
const CARD_LAYOUT = {
11+
ICON_ON_TOP: 'iconOnTop',
12+
ICON_ON_RIGHT: 'iconOnRight',
13+
};
914

1015
const propTypes = {
1116
/** An array of props that are pass to individual MenuItem components */
@@ -23,6 +28,10 @@ const propTypes = {
2328
/** Icon component */
2429
IconComponent: PropTypes.func,
2530

31+
/** Card layout that affects icon positioning, margins, sizes. */
32+
// eslint-disable-next-line rulesdir/prefer-underscore-method
33+
cardLayout: PropTypes.oneOf(Object.values(CARD_LAYOUT)),
34+
2635
/** Contents to display inside the section */
2736
children: PropTypes.node,
2837

@@ -38,6 +47,9 @@ const propTypes = {
3847
// eslint-disable-next-line react/forbid-prop-types
3948
subtitleStyles: PropTypes.arrayOf(PropTypes.object),
4049

50+
/** Whether the subtitle should have a muted style */
51+
subtitleMuted: PropTypes.bool,
52+
4153
/** Customize the Section container */
4254
// eslint-disable-next-line react/forbid-prop-types
4355
childrenStyles: PropTypes.arrayOf(PropTypes.object),
@@ -52,38 +64,45 @@ const defaultProps = {
5264
children: null,
5365
icon: null,
5466
IconComponent: null,
67+
cardLayout: CARD_LAYOUT.ICON_ON_RIGHT,
5568
containerStyles: [],
5669
iconContainerStyles: [],
5770
titleStyles: [],
5871
subtitleStyles: [],
72+
subtitleMuted: false,
5973
childrenStyles: [],
6074
subtitle: null,
6175
};
6276

63-
function Section({children, childrenStyles, containerStyles, icon, IconComponent, iconContainerStyles, menuItems, subtitle, subtitleStyles, title, titleStyles}) {
77+
function Section({children, childrenStyles, containerStyles, icon, IconComponent, cardLayout, iconContainerStyles, menuItems, subtitle, subtitleStyles, subtitleMuted, title, titleStyles}) {
6478
const styles = useThemeStyles();
79+
6580
return (
6681
<>
6782
<View style={[styles.pageWrapper, styles.cardSection, ...containerStyles]}>
68-
<View style={[styles.flexRow, styles.alignItemsCenter, styles.w100, ...titleStyles]}>
83+
{cardLayout === CARD_LAYOUT.ICON_ON_TOP && (
84+
<IconSection
85+
icon={icon}
86+
IconComponent={IconComponent}
87+
iconContainerStyles={[...iconContainerStyles, styles.alignSelfStart, styles.mb3]}
88+
/>
89+
)}
90+
<View style={[styles.flexRow, styles.alignItemsCenter, styles.w100, cardLayout === CARD_LAYOUT.ICON_ON_TOP && styles.mh1, ...titleStyles]}>
6991
<View style={[styles.flexShrink1]}>
7092
<Text style={[styles.textHeadline, styles.cardSectionTitle]}>{title}</Text>
7193
</View>
72-
<View style={[styles.flexGrow1, styles.flexRow, styles.justifyContentEnd, ...iconContainerStyles]}>
73-
{Boolean(icon) && (
74-
<Icon
75-
src={icon}
76-
height={68}
77-
width={68}
78-
/>
79-
)}
80-
{Boolean(IconComponent) && <IconComponent />}
81-
</View>
94+
{cardLayout === CARD_LAYOUT.ICON_ON_RIGHT && (
95+
<IconSection
96+
icon={icon}
97+
IconComponent={IconComponent}
98+
iconContainerStyles={iconContainerStyles}
99+
/>
100+
)}
82101
</View>
83102

84103
{Boolean(subtitle) && (
85-
<View style={[styles.flexRow, styles.alignItemsCenter, styles.w100, styles.mt4, ...subtitleStyles]}>
86-
<Text style={styles.textNormal}>{subtitle}</Text>
104+
<View style={[styles.flexRow, styles.alignItemsCenter, styles.w100, cardLayout === CARD_LAYOUT.ICON_ON_TOP ? [styles.mt1, styles.mh1] : styles.mt4, ...subtitleStyles]}>
105+
<Text style={[styles.textNormal, subtitleMuted && styles.colorMuted]}>{subtitle}</Text>
87106
</View>
88107
)}
89108

@@ -94,9 +113,9 @@ function Section({children, childrenStyles, containerStyles, icon, IconComponent
94113
</>
95114
);
96115
}
97-
98116
Section.displayName = 'Section';
99117
Section.propTypes = propTypes;
100118
Section.defaultProps = defaultProps;
101119

120+
export {CARD_LAYOUT};
102121
export default Section;

src/languages/en.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1489,8 +1489,9 @@ export default {
14891489
mustBeOnlineToViewMembers: 'You must be online in order to view members of this workspace.',
14901490
},
14911491
emptyWorkspace: {
1492-
title: 'Create a new workspace',
1493-
subtitle: "Workspaces are where you'll chat with your team, reimburse expenses, issue cards, send invoices, pay bills, and more — all in one place.",
1492+
title: 'Create a workspace',
1493+
subtitle: 'Manage business expenses, issue cards, send invoices, and more.',
1494+
createAWorkspaceCTA: 'Get Started',
14941495
features: {
14951496
trackAndCollect: 'Track and collect receipts',
14961497
companyCards: 'Company credit cards',

src/languages/es.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,8 +1512,9 @@ export default {
15121512
mustBeOnlineToViewMembers: 'Debes estar en línea para poder ver los miembros de este espacio de trabajo.',
15131513
},
15141514
emptyWorkspace: {
1515-
title: 'Crear un nuevo espacio de trabajo',
1516-
subtitle: 'En los espacios de trabajo es donde puedes chatear con tu equipo, reembolsar gastos, emitir tarjetas, enviar y pagar facturas y mas — todo en un mismo lugar',
1515+
title: 'Crea un espacio de trabajo',
1516+
subtitle: 'Administra gastos de empresa, emite tarjetas, envía facturas y mucho más.',
1517+
createAWorkspaceCTA: 'Comenzar',
15171518
features: {
15181519
trackAndCollect: 'Organiza recibos',
15191520
companyCards: 'Tarjetas de crédito corporativas',
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React from 'react';
2+
import Button from '@components/Button';
3+
import * as Illustrations from '@components/Icon/Illustrations';
4+
import Section, {CARD_LAYOUT} from '@components/Section';
5+
import useLocalize from '@hooks/useLocalize';
6+
import useThemeStyles from '@hooks/useThemeStyles';
7+
8+
function WorkspaceCardCreateAWorkspace() {
9+
const styles = useThemeStyles();
10+
const {translate} = useLocalize();
11+
12+
return (
13+
<Section
14+
title={translate('workspace.emptyWorkspace.title')}
15+
icon={Illustrations.HotDogStand}
16+
cardLayout={CARD_LAYOUT.ICON_ON_TOP}
17+
subtitle={translate('workspace.emptyWorkspace.subtitle')}
18+
subtitleMuted
19+
containerStyles={[styles.highlightBG]}
20+
>
21+
<Button
22+
text={translate('workspace.emptyWorkspace.createAWorkspaceCTA')}
23+
style={styles.mt5}
24+
success
25+
medium
26+
/>
27+
</Section>
28+
);
29+
}
30+
31+
WorkspaceCardCreateAWorkspace.displayName = 'WorkspaceCardNoVBAView';
32+
33+
export default WorkspaceCardCreateAWorkspace;

src/styles/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -938,10 +938,14 @@ const styles = (theme: ThemeColors) =>
938938
overflow: 'hidden',
939939
},
940940

941-
calendarDayContainerSelected: {
941+
buttonDefaultBG: {
942942
backgroundColor: theme.buttonDefaultBG,
943943
},
944944

945+
buttonHoveredBG: {
946+
backgroundColor: theme.buttonHoveredBG,
947+
},
948+
945949
autoGrowHeightInputContainer: (textInputHeight: number, minHeight: number, maxHeight: number) =>
946950
({
947951
height: lodashClamp(textInputHeight, minHeight, maxHeight),
@@ -1952,10 +1956,6 @@ const styles = (theme: ThemeColors) =>
19521956
alignSelf: 'flex-end',
19531957
},
19541958

1955-
hoveredButton: {
1956-
backgroundColor: theme.buttonHoveredBG,
1957-
},
1958-
19591959
composerSizeButton: {
19601960
alignSelf: 'center',
19611961
height: 32,

0 commit comments

Comments
 (0)