@takaro/lib-components 0.0.15 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile.dev +1 -1
- package/package.json +5 -7
- package/src/components/actions/Button/Button.test.tsx +2 -1
- package/src/components/actions/Button/__snapshots__/Button.test.tsx.snap +18 -0
- package/src/components/actions/Button/style.ts +1 -0
- package/src/components/actions/DropdownButton/index.tsx +51 -36
- package/src/components/actions/IconButton/IconButton.test.tsx +3 -2
- package/src/components/actions/IconButton/__snapshots__/IconButton.test.tsx.snap +18 -0
- package/src/components/actions/IconButton/getIconSize.ts +16 -0
- package/src/components/actions/IconButton/index.tsx +2 -16
- package/src/components/actions/ToggleButton/ToggleButtonGroup.tsx +0 -1
- package/src/components/charts/GeoMercator/index.tsx +3 -3
- package/src/components/charts/RadialBarChart/index.tsx +1 -1
- package/src/components/data/Chip/Chip.test.tsx +3 -2
- package/src/components/data/Chip/__snapshots__/Chip.test.tsx.snap +38 -0
- package/src/components/data/Console/Console.tsx +1 -7
- package/src/components/data/Console/ConsoleLine/index.tsx +1 -1
- package/src/components/data/Console/constants.ts +6 -0
- package/src/components/data/Table/index.tsx +30 -1
- package/src/components/data/Table/subcomponents/Filter/field.tsx +2 -3
- package/src/components/data/Table/subcomponents/Filter/index.tsx +1 -11
- package/src/components/data/Table/subcomponents/Filter/types.ts +10 -0
- package/src/components/data/Table/subcomponents/Pagination/PageSizeSelect.tsx +0 -1
- package/src/components/data/index.ts +0 -3
- package/src/components/dialogs/Dialog/DialogBody.tsx +1 -0
- package/src/components/feedback/Alert/Alert.test.tsx +2 -1
- package/src/components/feedback/Alert/__snapshots__/Alert.test.tsx.snap +44 -0
- package/src/components/feedback/IconTooltip/IconTooltip.stories.tsx +22 -0
- package/src/components/feedback/IconTooltip/index.tsx +50 -0
- package/src/components/feedback/Loaders/Loading.test.tsx +2 -1
- package/src/components/feedback/Loaders/Spinner.test.tsx +2 -1
- package/src/components/feedback/Loaders/__snapshots__/Loading.test.tsx.snap +141 -0
- package/src/components/feedback/Loaders/__snapshots__/Spinner.test.tsx.snap +10 -0
- package/src/components/feedback/NetworkDetector/NetworkDetector.test.tsx +2 -1
- package/src/components/feedback/NetworkDetector/__snapshots__/NetworkDetector.test.tsx.snap +3 -0
- package/src/components/feedback/NotificationBanner/NotificationBanner.test.tsx +2 -1
- package/src/components/feedback/NotificationBanner/__snapshots__/NotificationBanner.test.tsx.snap +3 -0
- package/src/components/feedback/ProgressBar/ProgressBar.stories.tsx +2 -1
- package/src/components/feedback/ProgressBar/index.tsx +58 -24
- package/src/components/feedback/Skeleton/Skeleton.test.tsx +2 -1
- package/src/components/feedback/Skeleton/__snapshots__/Skeleton.test.tsx.snap +9 -0
- package/src/components/feedback/Tooltip/TooltipContent.tsx +2 -2
- package/src/components/feedback/Tooltip/useTooltip.tsx +3 -0
- package/src/components/feedback/index.ts +2 -1
- package/src/components/feedback/snacks/Drawer/index.tsx +15 -4
- package/src/components/inputs/Date/DateRangePicker/Context.tsx +1 -1
- package/src/components/inputs/Date/DateRangePicker/Generic.tsx +2 -1
- package/src/components/inputs/Date/DateRangePicker/QuickSelect/index.tsx +1 -17
- package/src/components/inputs/Date/subcomponents/RelativePicker/index.tsx +1 -17
- package/src/components/inputs/Date/types.ts +14 -0
- package/src/components/inputs/DurationField/Generic.tsx +0 -1
- package/src/components/inputs/TextField/Controlled.tsx +1 -1
- package/src/components/inputs/TextField/Generic.tsx +79 -56
- package/src/components/inputs/TextField/TextField.stories.tsx +21 -1
- package/src/components/inputs/TextField/style.ts +6 -0
- package/src/components/inputs/TextField/util.ts +7 -2
- package/src/components/inputs/index.ts +1 -0
- package/src/components/inputs/layout/Description.tsx +0 -5
- package/src/components/inputs/layout/Label/style.ts +0 -1
- package/src/components/inputs/layout/index.ts +3 -1
- package/src/components/inputs/layout/setAriaDescribedBy.ts +3 -0
- package/src/components/inputs/selects/SelectField/Controlled.tsx +0 -2
- package/src/components/inputs/selects/SelectField/Generic/index.tsx +11 -14
- package/src/components/inputs/selects/SelectQueryField/Controlled.tsx +2 -2
- package/src/components/inputs/selects/SelectQueryField/Generic/index.tsx +115 -40
- package/src/components/inputs/selects/SelectQueryField/style.ts +0 -1
- package/src/components/inputs/selects/SubComponents/Option.tsx +1 -1
- package/src/components/inputs/selects/sharedStyle.ts +0 -2
- package/src/components/layout/Container/index.ts +1 -1
- package/src/components/layout/index.ts +1 -1
- package/src/components/navigation/HorizontalNav/HorizontalNav.stories.tsx +6 -22
- package/src/components/navigation/HorizontalNav/index.tsx +1 -1
- package/src/components/navigation/HorizontalNav/style.ts +28 -3
- package/src/components/navigation/Steppers/SlimStepper/Stepper.stories.tsx +2 -1
- package/src/components/navigation/Steppers/SlimStepper/index.tsx +4 -4
- package/src/components/navigation/Steppers/Stepper/Stepper.stories.tsx +2 -1
- package/src/components/navigation/Steppers/Stepper/index.tsx +4 -4
- package/src/components/navigation/Steppers/context.tsx +2 -49
- package/src/components/navigation/Steppers/provider.tsx +18 -0
- package/src/components/navigation/Steppers/useStepper.ts +34 -0
- package/src/components/navigation/index.ts +2 -1
- package/src/components/other/ActionMenu/index.tsx +20 -11
- package/src/components/other/ActionMenu/style.ts +2 -2
- package/src/components/other/ClipBoard/ClipBoard.test.tsx +2 -1
- package/src/components/other/ClipBoard/__snapshots__/ClipBoard.test.tsx.snap +36 -0
- package/src/components/other/Empty/Empty.test.tsx +5 -2
- package/src/components/other/Empty/__snapshots__/Empty.test.tsx.snap +21 -0
- package/src/components/other/PermissionsGuard/hasPermissionsHelper.ts +18 -0
- package/src/components/other/PermissionsGuard/index.tsx +1 -17
- package/src/components/other/Plan/Plan.stories.tsx +21 -0
- package/src/components/other/Plan/index.tsx +93 -0
- package/src/components/other/Plan/style.ts +61 -0
- package/src/components/other/Usage/Usage.stories.tsx +21 -0
- package/src/components/other/Usage/Usage.tsx +35 -0
- package/src/components/other/Usage/UsageCard.stories.tsx +31 -0
- package/src/components/other/Usage/UsageCard.tsx +88 -0
- package/src/components/other/index.ts +10 -2
- package/src/components/visual/Card/index.tsx +11 -8
- package/src/components/visual/Divider/Divider.test.tsx +2 -1
- package/src/components/visual/Divider/__snapshots__/Divider.test.tsx.snap +13 -0
- package/src/components/visual/Divider/index.tsx +9 -6
- package/src/errors/errors.ts +1 -1
- package/src/helpers/regexprs.ts +2 -1
- package/tsconfig.json +0 -5
- package/vite.config.mts +17 -0
- package/src/components/data/LinkCard/index.tsx +0 -28
- package/src/components/feedback/QuestionTooltip/QuestionTooltip.stories.tsx +0 -20
- package/src/components/feedback/QuestionTooltip/index.tsx +0 -35
|
@@ -4,7 +4,8 @@ export type { StepperProps } from './Steppers/Stepper';
|
|
|
4
4
|
export { SlimStepper } from './Steppers/SlimStepper';
|
|
5
5
|
export type { SlimStepperProps } from './Steppers/SlimStepper';
|
|
6
6
|
|
|
7
|
-
export { StepperProvider
|
|
7
|
+
export { StepperProvider } from './Steppers/provider';
|
|
8
|
+
export { useStepper } from './Steppers/useStepper';
|
|
8
9
|
|
|
9
10
|
export { IconNav } from './IconNav';
|
|
10
11
|
export type { IconNavProps } from './IconNav';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Children, forwardRef,
|
|
1
|
+
import { Children, forwardRef, FC, ReactElement, PropsWithChildren } from 'react';
|
|
2
2
|
import { Container, Item } from './style';
|
|
3
3
|
import { AiOutlineCheck as CheckMarkIcon } from 'react-icons/ai';
|
|
4
4
|
import { Elevation } from '../../../styled/';
|
|
@@ -9,17 +9,16 @@ export interface ActionMenuProps {
|
|
|
9
9
|
y: number | null;
|
|
10
10
|
strategy: 'absolute' | 'fixed';
|
|
11
11
|
};
|
|
12
|
-
|
|
12
|
+
selected: number;
|
|
13
|
+
setSelected: (selected: number) => void;
|
|
13
14
|
children: ReactElement | ReactElement[];
|
|
14
15
|
elevation?: Elevation;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export const ActionMenu = forwardRef<HTMLUListElement, ActionMenuProps>(function ActionMenu(
|
|
18
|
-
{ attributes, children,
|
|
19
|
+
{ attributes, children, selected, setSelected, elevation = 4 },
|
|
19
20
|
ref,
|
|
20
21
|
) {
|
|
21
|
-
const [selected, setSelected] = selectedState;
|
|
22
|
-
|
|
23
22
|
return (
|
|
24
23
|
<Container
|
|
25
24
|
elevation={elevation}
|
|
@@ -31,14 +30,22 @@ export const ActionMenu = forwardRef<HTMLUListElement, ActionMenuProps>(function
|
|
|
31
30
|
ref={ref}
|
|
32
31
|
>
|
|
33
32
|
{Children.map(children, (child: ReactElement<ActionProps>, idx) => (
|
|
34
|
-
<Item
|
|
33
|
+
<Item
|
|
34
|
+
aria-disabled={child.props.disabled}
|
|
35
|
+
onClick={() => {
|
|
36
|
+
if (child.props.disabled) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
setSelected(idx);
|
|
40
|
+
}}
|
|
41
|
+
>
|
|
35
42
|
{child}
|
|
36
43
|
{selected === idx ? (
|
|
37
44
|
<div style={{ display: 'flex', alignItems: 'center' }}>
|
|
38
|
-
<CheckMarkIcon size={15} />
|
|
45
|
+
<CheckMarkIcon className="checkmark" size={15} />
|
|
39
46
|
</div>
|
|
40
47
|
) : (
|
|
41
|
-
<div className="checkmark-placeholder"
|
|
48
|
+
<div className="checkmark-placeholder" />
|
|
42
49
|
)}
|
|
43
50
|
</Item>
|
|
44
51
|
))}
|
|
@@ -49,8 +56,10 @@ export const ActionMenu = forwardRef<HTMLUListElement, ActionMenuProps>(function
|
|
|
49
56
|
interface ActionProps {
|
|
50
57
|
onClick: () => unknown;
|
|
51
58
|
text: string;
|
|
52
|
-
|
|
59
|
+
disabled?: boolean;
|
|
53
60
|
}
|
|
54
|
-
|
|
55
|
-
|
|
61
|
+
|
|
62
|
+
export const Action: FC<PropsWithChildren<ActionProps>> = ({ children, disabled = false }) => {
|
|
63
|
+
const _ = disabled;
|
|
64
|
+
return children;
|
|
56
65
|
};
|
|
@@ -36,12 +36,12 @@ export const Item = styled.li`
|
|
|
36
36
|
color: white;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
svg {
|
|
39
|
+
svg.checkmark {
|
|
40
40
|
fill: white;
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
svg {
|
|
44
|
+
svg.checkmark {
|
|
45
45
|
margin-left: ${({ theme }) => theme.spacing[1]};
|
|
46
46
|
fill: ${({ theme }): string => theme.colors.text};
|
|
47
47
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ClipBoard } from '.';
|
|
2
|
-
import { render } from 'test
|
|
2
|
+
import { render } from '../../../test/testUtils';
|
|
3
|
+
import { expect, it } from 'vitest';
|
|
3
4
|
|
|
4
5
|
it('Should render <ClipBoard />', () => {
|
|
5
6
|
const { container } = render(<ClipBoard text="ClipBoard text" />);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`Should render <ClipBoard /> 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="sc-aXZVg jSPpZO"
|
|
7
|
+
role="button"
|
|
8
|
+
tabindex="-1"
|
|
9
|
+
>
|
|
10
|
+
<input
|
|
11
|
+
aria-describedby="clipboard-input-error"
|
|
12
|
+
aria-invalid="false"
|
|
13
|
+
readonly=""
|
|
14
|
+
type="text"
|
|
15
|
+
value="ClipBoard text"
|
|
16
|
+
/>
|
|
17
|
+
<div
|
|
18
|
+
class="sc-gEvEer bneOhB"
|
|
19
|
+
>
|
|
20
|
+
<svg
|
|
21
|
+
fill="currentColor"
|
|
22
|
+
height="20"
|
|
23
|
+
stroke="currentColor"
|
|
24
|
+
stroke-width="0"
|
|
25
|
+
viewBox="0 0 1024 1024"
|
|
26
|
+
width="20"
|
|
27
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
28
|
+
>
|
|
29
|
+
<path
|
|
30
|
+
d="M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z"
|
|
31
|
+
/>
|
|
32
|
+
</svg>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
`;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Empty } from '.';
|
|
2
|
-
import { render } from 'test
|
|
2
|
+
import { render } from '../../../test/testUtils.tsx';
|
|
3
|
+
import { expect, it } from 'vitest';
|
|
3
4
|
|
|
4
5
|
it('Should render <Empty />', () => {
|
|
5
|
-
const { container } = render(
|
|
6
|
+
const { container } = render(
|
|
7
|
+
<Empty header="this is the header" description="this is the description" actions={[]} />,
|
|
8
|
+
);
|
|
6
9
|
expect(container).toMatchSnapshot();
|
|
7
10
|
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`Should render <Empty /> 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="sc-aXZVg csqXIR"
|
|
7
|
+
>
|
|
8
|
+
<h2>
|
|
9
|
+
this is the header
|
|
10
|
+
</h2>
|
|
11
|
+
<p
|
|
12
|
+
class="sc-eqUAAy flGMfW"
|
|
13
|
+
>
|
|
14
|
+
this is the description
|
|
15
|
+
</p>
|
|
16
|
+
<div
|
|
17
|
+
class="sc-gEvEer eufunr"
|
|
18
|
+
/>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
`;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { PERMISSIONS } from '@takaro/apiclient';
|
|
2
|
+
|
|
3
|
+
type PermissionsSet = PERMISSIONS | PERMISSIONS[];
|
|
4
|
+
export type RequiredPermissions = PermissionsSet[];
|
|
5
|
+
|
|
6
|
+
export const hasPermissionHelper = (
|
|
7
|
+
userPermissions: PERMISSIONS[],
|
|
8
|
+
requiredPermissions: RequiredPermissions,
|
|
9
|
+
): boolean => {
|
|
10
|
+
if (userPermissions.includes(PERMISSIONS.Root)) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
return requiredPermissions.some((permissionSet) =>
|
|
14
|
+
Array.isArray(permissionSet)
|
|
15
|
+
? permissionSet.every((permission) => userPermissions.includes(permission))
|
|
16
|
+
: userPermissions.includes(permissionSet as PERMISSIONS),
|
|
17
|
+
);
|
|
18
|
+
};
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { FC, PropsWithChildren, ReactElement, useMemo } from 'react';
|
|
2
2
|
import { PERMISSIONS } from '@takaro/apiclient';
|
|
3
|
-
|
|
4
|
-
type PermissionsSet = PERMISSIONS | PERMISSIONS[];
|
|
5
|
-
export type RequiredPermissions = PermissionsSet[];
|
|
3
|
+
import { RequiredPermissions, hasPermissionHelper } from './hasPermissionsHelper';
|
|
6
4
|
|
|
7
5
|
export interface PermissionsGuardProps {
|
|
8
6
|
requiredPermissions: RequiredPermissions;
|
|
@@ -10,20 +8,6 @@ export interface PermissionsGuardProps {
|
|
|
10
8
|
fallback?: ReactElement;
|
|
11
9
|
}
|
|
12
10
|
|
|
13
|
-
export const hasPermissionHelper = (
|
|
14
|
-
userPermissions: PERMISSIONS[],
|
|
15
|
-
requiredPermissions: RequiredPermissions,
|
|
16
|
-
): boolean => {
|
|
17
|
-
if (userPermissions.includes(PERMISSIONS.Root)) {
|
|
18
|
-
return true;
|
|
19
|
-
}
|
|
20
|
-
return requiredPermissions.some((permissionSet) =>
|
|
21
|
-
Array.isArray(permissionSet)
|
|
22
|
-
? permissionSet.every((permission) => userPermissions.includes(permission))
|
|
23
|
-
: userPermissions.includes(permissionSet as PERMISSIONS),
|
|
24
|
-
);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
11
|
export const PermissionsGuard: FC<PropsWithChildren<PermissionsGuardProps>> = ({
|
|
28
12
|
userPermissions,
|
|
29
13
|
requiredPermissions,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Meta, StoryFn } from '@storybook/react';
|
|
3
|
+
import { Plan, PlanProps } from '.';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: 'Other/Plan',
|
|
7
|
+
component: Plan,
|
|
8
|
+
args: {
|
|
9
|
+
title: 'Hobby plan',
|
|
10
|
+
description:
|
|
11
|
+
'Lorem ipsum dolor sit amet consect etur adipisicing elit. Itaque amet indis perferendis blanditiis repellendus etur quidem assumenda.',
|
|
12
|
+
price: '20',
|
|
13
|
+
features: ['Lorem ipsum', 'dolor sit amet', 'etur adipisicing elit', 'Itaque amet indis perferendis blanditiis '],
|
|
14
|
+
buttonText: 'Get access',
|
|
15
|
+
to: 'https://takaro.io',
|
|
16
|
+
},
|
|
17
|
+
} as Meta<PlanProps>;
|
|
18
|
+
|
|
19
|
+
export const Default: StoryFn<PlanProps> = (args) => {
|
|
20
|
+
return <Plan {...args} />;
|
|
21
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { Button, Card, Chip, Divider } from '../../../components';
|
|
3
|
+
import { AiOutlineCheck as CheckmarkIcon } from 'react-icons/ai';
|
|
4
|
+
import { useTheme } from '../../../hooks';
|
|
5
|
+
import { InnerContainer, PriceContainer, FeatureList, FeaturesContainer } from './style';
|
|
6
|
+
|
|
7
|
+
export interface PlanProps {
|
|
8
|
+
to: string;
|
|
9
|
+
title: string;
|
|
10
|
+
description: string;
|
|
11
|
+
features: string[];
|
|
12
|
+
price?: string;
|
|
13
|
+
buttonText: string;
|
|
14
|
+
highlight?: boolean;
|
|
15
|
+
currency?: string;
|
|
16
|
+
period?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const Plan: FC<PlanProps> = ({
|
|
20
|
+
to,
|
|
21
|
+
title,
|
|
22
|
+
description,
|
|
23
|
+
features,
|
|
24
|
+
price,
|
|
25
|
+
buttonText,
|
|
26
|
+
highlight = false,
|
|
27
|
+
currency,
|
|
28
|
+
period,
|
|
29
|
+
}) => {
|
|
30
|
+
const theme = useTheme();
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Card
|
|
34
|
+
style={{
|
|
35
|
+
maxWidth: '1000px',
|
|
36
|
+
width: '100%',
|
|
37
|
+
margin: '0 auto',
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
40
|
+
<Card.Body>
|
|
41
|
+
<InnerContainer>
|
|
42
|
+
<FeaturesContainer>
|
|
43
|
+
<h1 style={{}}>
|
|
44
|
+
{title}
|
|
45
|
+
{highlight && <Chip color="primary" variant="outline" label="Most popular" />}
|
|
46
|
+
</h1>
|
|
47
|
+
<p style={{ marginBottom: '3rem' }}>{description}</p>
|
|
48
|
+
<Divider
|
|
49
|
+
// eslint-disable-next-line quotes
|
|
50
|
+
label={{ text: "What's included", labelPosition: 'center', color: 'primary' }}
|
|
51
|
+
fullWidth
|
|
52
|
+
size="large"
|
|
53
|
+
/>
|
|
54
|
+
<FeatureList>
|
|
55
|
+
{features.map((feature) => (
|
|
56
|
+
<li key={feature}>
|
|
57
|
+
<CheckmarkIcon fill={theme.colors.primary} style={{ marginRight: '0.5rem' }} /> {feature}
|
|
58
|
+
</li>
|
|
59
|
+
))}
|
|
60
|
+
</FeatureList>
|
|
61
|
+
</FeaturesContainer>
|
|
62
|
+
{/* right side*/}
|
|
63
|
+
<PriceContainer highlight={highlight}>
|
|
64
|
+
<div>
|
|
65
|
+
{price && (
|
|
66
|
+
<p
|
|
67
|
+
style={{
|
|
68
|
+
display: 'flex',
|
|
69
|
+
alignItems: 'baseline',
|
|
70
|
+
justifyContent: 'center',
|
|
71
|
+
|
|
72
|
+
fontSize: theme.fontSize.mediumLarge,
|
|
73
|
+
fontWeight: 600,
|
|
74
|
+
columnGap: theme.spacing['0_5'],
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
<span style={{ fontSize: theme.fontSize.huge, fontWeight: 800 }}>{price}</span>
|
|
78
|
+
<span style={{ fontSize: theme.fontSize.small }}>
|
|
79
|
+
{currency ? currency.toUpperCase() : ''} / {period}
|
|
80
|
+
</span>
|
|
81
|
+
</p>
|
|
82
|
+
)}
|
|
83
|
+
<a style={{ width: '100%' }} className="button" href={to} target="_blank" rel="noreferrer">
|
|
84
|
+
<Button fullWidth text={buttonText} />
|
|
85
|
+
</a>
|
|
86
|
+
<p></p>
|
|
87
|
+
</div>
|
|
88
|
+
</PriceContainer>
|
|
89
|
+
</InnerContainer>
|
|
90
|
+
</Card.Body>
|
|
91
|
+
</Card>
|
|
92
|
+
);
|
|
93
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { styled } from '../../../styled';
|
|
2
|
+
|
|
3
|
+
export const InnerContainer = styled.div`
|
|
4
|
+
display: grid;
|
|
5
|
+
grid-template-columns: 1fr;
|
|
6
|
+
|
|
7
|
+
@media (max-width: 1200px) {
|
|
8
|
+
grid-template-columns: 450px 1fr;
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
export const PriceContainer = styled.div<{ highlight: boolean }>`
|
|
13
|
+
background-color: ${({ theme }) => theme.colors.background};
|
|
14
|
+
border-radius: ${({ theme }) => theme.borderRadius.medium};
|
|
15
|
+
border-width: 1px;
|
|
16
|
+
border-style: solid;
|
|
17
|
+
border-color: ${({ highlight, theme }) => (highlight ? theme.colors.primary : theme.colors.backgroundAccent)};
|
|
18
|
+
padding: ${({ theme }) => theme.spacing[4]};
|
|
19
|
+
|
|
20
|
+
& > div:first-child {
|
|
21
|
+
justify-content: center;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
height: 100%;
|
|
26
|
+
min-width: fit-content;
|
|
27
|
+
|
|
28
|
+
p:last-child {
|
|
29
|
+
font-size: ${({ theme }) => theme.fontSize.tiny};
|
|
30
|
+
color: ${({ theme }) => theme.colors.textAlt};
|
|
31
|
+
margin-top: ${({ theme }) => theme.spacing['1_5']};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
export const FeaturesContainer = styled.div`
|
|
37
|
+
flex: 1 1 auto;
|
|
38
|
+
padding: 0 ${({ theme }) => theme.spacing[2]};
|
|
39
|
+
|
|
40
|
+
h1 {
|
|
41
|
+
margin-bottom: ${({ theme }) => theme.spacing['1_5']};
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
justify-content: space-between;
|
|
45
|
+
}
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
export const FeatureList = styled.ul`
|
|
49
|
+
display: grid;
|
|
50
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
51
|
+
gap: 1.5rem;
|
|
52
|
+
margin-bottom: 1.5rem;
|
|
53
|
+
|
|
54
|
+
li {
|
|
55
|
+
column-gap: ${({ theme }) => theme.spacing['0_75']};
|
|
56
|
+
display: flex;
|
|
57
|
+
align-items: center;
|
|
58
|
+
justify-content: center;
|
|
59
|
+
text-transform: capitalize;
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Meta, StoryFn } from '@storybook/react';
|
|
3
|
+
import { Usage, UsageProps } from './Usage';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: 'Other/Usage',
|
|
7
|
+
component: Usage,
|
|
8
|
+
args: {
|
|
9
|
+
unit: 'Functions',
|
|
10
|
+
total: 5,
|
|
11
|
+
value: 1,
|
|
12
|
+
},
|
|
13
|
+
} as Meta<UsageProps>;
|
|
14
|
+
|
|
15
|
+
export const Default: StoryFn<UsageProps> = (args) => {
|
|
16
|
+
return (
|
|
17
|
+
<div style={{ width: '250px' }}>
|
|
18
|
+
<Usage {...args} />
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { AlertVariants, Color } from '../../../styled';
|
|
2
|
+
import { ProgressBar } from '../../../components';
|
|
3
|
+
import { FC } from 'react';
|
|
4
|
+
|
|
5
|
+
export interface UsageProps {
|
|
6
|
+
unit?: string;
|
|
7
|
+
value: number;
|
|
8
|
+
total: number;
|
|
9
|
+
progressBarColor?: Color | AlertVariants;
|
|
10
|
+
minFill?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const Usage: FC<UsageProps> = ({ value, total, unit, progressBarColor = 'primary', minFill }) => {
|
|
14
|
+
const percentage = (value / total) * 100;
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div style={{ minWidth: '175px' }}>
|
|
18
|
+
<div
|
|
19
|
+
style={{
|
|
20
|
+
width: '100%',
|
|
21
|
+
display: 'flex',
|
|
22
|
+
justifyContent: 'space-between',
|
|
23
|
+
alignItems: 'center',
|
|
24
|
+
paddingRight: '0.25rem',
|
|
25
|
+
}}
|
|
26
|
+
>
|
|
27
|
+
<p>{unit}</p>
|
|
28
|
+
<p>
|
|
29
|
+
{value} of {total}
|
|
30
|
+
</p>
|
|
31
|
+
</div>
|
|
32
|
+
<ProgressBar mode="determinate" value={percentage} size="small" color={progressBarColor} minFill={minFill} />
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Meta, StoryFn } from '@storybook/react';
|
|
3
|
+
import { UsageCard, UsageCardProps } from './UsageCard';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: 'Other/UsageCard',
|
|
7
|
+
component: UsageCard,
|
|
8
|
+
args: {
|
|
9
|
+
title: 'Functions included in your plan',
|
|
10
|
+
info: 'Here we can add some extra details?',
|
|
11
|
+
description: 'Here we can add some extra details about something.',
|
|
12
|
+
unit: 'Functions',
|
|
13
|
+
total: 100000,
|
|
14
|
+
value: 52329,
|
|
15
|
+
},
|
|
16
|
+
} as Meta<UsageCardProps>;
|
|
17
|
+
|
|
18
|
+
export const Default: StoryFn<UsageCardProps> = (args) => {
|
|
19
|
+
return (
|
|
20
|
+
<>
|
|
21
|
+
<div style={{ marginBottom: '2rem' }}>
|
|
22
|
+
<h1 style={{ fontSize: '3.5rem', fontWeight: 500 }}>Summary</h1>
|
|
23
|
+
<p>Resets on Sept 1st, 2025</p>
|
|
24
|
+
</div>
|
|
25
|
+
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '2rem' }}>
|
|
26
|
+
<UsageCard {...args} />
|
|
27
|
+
<UsageCard {...args} />
|
|
28
|
+
</div>
|
|
29
|
+
</>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { styled } from '../../../styled';
|
|
2
|
+
import { useTheme } from '../../../hooks';
|
|
3
|
+
import { Card, ProgressBar, IconTooltip } from '../../../components';
|
|
4
|
+
|
|
5
|
+
import { AiOutlineQuestion as QuestionIcon } from 'react-icons/ai';
|
|
6
|
+
import { FC } from 'react';
|
|
7
|
+
import { UsageProps } from './Usage';
|
|
8
|
+
|
|
9
|
+
const TitleContainer = styled.div<{ margin: boolean }>`
|
|
10
|
+
display: flex;
|
|
11
|
+
align-items: center;
|
|
12
|
+
justify-content: space-between;
|
|
13
|
+
margin-bottom: ${({ margin, theme }) => (margin ? theme.spacing['2'] : 0)};
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
const DetailsContainer = styled.div`
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
justify-content: space-between;
|
|
20
|
+
margin-bottom: ${({ theme }) => theme.spacing[1]};
|
|
21
|
+
|
|
22
|
+
h2 {
|
|
23
|
+
font-size: 2.25rem;
|
|
24
|
+
font-weight: 600;
|
|
25
|
+
}
|
|
26
|
+
span {
|
|
27
|
+
backgroundcolor: ${({ theme }) => theme.spacing[1]};
|
|
28
|
+
border: 1px solid ${({ theme }) => theme.colors.backgroundAccent};
|
|
29
|
+
font-size: ${({ theme }) => theme.fontSize.mediumLarge};
|
|
30
|
+
padding: ${({ theme }) => `${theme.spacing['0_25']} ${theme.spacing['0_75']}`};
|
|
31
|
+
border-radius: ${({ theme }) => theme.borderRadius.small};
|
|
32
|
+
}
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
export interface UsageCardProps extends UsageProps {
|
|
36
|
+
title: string;
|
|
37
|
+
description?: string;
|
|
38
|
+
info?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const UsageCard: FC<UsageCardProps> = ({
|
|
42
|
+
title,
|
|
43
|
+
value,
|
|
44
|
+
total,
|
|
45
|
+
info,
|
|
46
|
+
unit,
|
|
47
|
+
description,
|
|
48
|
+
progressBarColor,
|
|
49
|
+
minFill,
|
|
50
|
+
}) => {
|
|
51
|
+
const theme = useTheme();
|
|
52
|
+
const used_percentage = Math.min((value / total) * 100, 100);
|
|
53
|
+
const remaining = Math.max(total - value, 0);
|
|
54
|
+
const formatter = new Intl.NumberFormat('en', {
|
|
55
|
+
notation: 'compact',
|
|
56
|
+
compactDisplay: 'short',
|
|
57
|
+
maximumFractionDigits: 1,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<Card>
|
|
62
|
+
<Card.Body>
|
|
63
|
+
<TitleContainer margin={description ? false : true}>
|
|
64
|
+
<h3>{title}</h3>
|
|
65
|
+
{info && (
|
|
66
|
+
<IconTooltip placement="top" color="background" icon={<QuestionIcon />}>
|
|
67
|
+
{info}
|
|
68
|
+
</IconTooltip>
|
|
69
|
+
)}
|
|
70
|
+
</TitleContainer>
|
|
71
|
+
{description && <p style={{ color: theme.colors.textAlt, marginBottom: theme.spacing[2] }}>{description}</p>}
|
|
72
|
+
<DetailsContainer>
|
|
73
|
+
<h2>
|
|
74
|
+
{formatter.format(value)} / {formatter.format(total)} {unit}
|
|
75
|
+
</h2>
|
|
76
|
+
<span>{formatter.format(remaining)} remaining</span>
|
|
77
|
+
</DetailsContainer>
|
|
78
|
+
<ProgressBar
|
|
79
|
+
mode="determinate"
|
|
80
|
+
value={used_percentage}
|
|
81
|
+
size="medium"
|
|
82
|
+
color={progressBarColor}
|
|
83
|
+
minFill={minFill}
|
|
84
|
+
/>
|
|
85
|
+
</Card.Body>
|
|
86
|
+
</Card>
|
|
87
|
+
);
|
|
88
|
+
};
|
|
@@ -12,8 +12,16 @@ export type { ActionMenuProps } from './ActionMenu';
|
|
|
12
12
|
|
|
13
13
|
export { CollapseList } from './CollapseList';
|
|
14
14
|
|
|
15
|
-
export { PermissionsGuard
|
|
16
|
-
export
|
|
15
|
+
export { PermissionsGuard } from './PermissionsGuard';
|
|
16
|
+
export { hasPermissionHelper } from './PermissionsGuard/hasPermissionsHelper';
|
|
17
|
+
export type { RequiredPermissions } from './PermissionsGuard/hasPermissionsHelper';
|
|
18
|
+
export type { PermissionsGuardProps } from './PermissionsGuard';
|
|
19
|
+
|
|
20
|
+
export { Plan } from './Plan';
|
|
21
|
+
export type { PlanProps } from './Plan';
|
|
22
|
+
|
|
23
|
+
export { UsageCard } from './Usage/UsageCard';
|
|
24
|
+
export type { UsageCardProps } from './Usage/UsageCard';
|
|
17
25
|
|
|
18
26
|
export { Collapsible } from './Collapsible';
|
|
19
27
|
export type { CollapsibleProps } from './Collapsible';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { forwardRef, HTMLProps, PropsWithChildren } from 'react';
|
|
2
2
|
import { styled } from '../../../styled';
|
|
3
3
|
import { CardTitle } from './CardTitle';
|
|
4
4
|
import { CardBody } from './CardBody';
|
|
@@ -38,22 +38,25 @@ interface SubComponentTypes {
|
|
|
38
38
|
Body: typeof CardBody;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
children,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}) {
|
|
41
|
+
const _Card = forwardRef<HTMLDivElement, PropsWithChildren<CardProps>>(function Card(
|
|
42
|
+
{ children, variant = 'default', ...props },
|
|
43
|
+
ref,
|
|
44
|
+
) {
|
|
46
45
|
const canClick = 'onClick' in props;
|
|
47
46
|
const { className, ...restProps } = props;
|
|
48
47
|
|
|
49
48
|
return (
|
|
50
49
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
51
50
|
// @ts-ignore
|
|
52
|
-
<Container canClick={canClick} variant={variant} className={className} {...restProps}>
|
|
51
|
+
<Container ref={ref} canClick={canClick} variant={variant} className={className} {...restProps}>
|
|
53
52
|
{children}
|
|
54
53
|
</Container>
|
|
55
54
|
);
|
|
56
|
-
};
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// TODO: type it correctly instead
|
|
58
|
+
type CardType = typeof _Card & SubComponentTypes;
|
|
59
|
+
export const Card = _Card as CardType;
|
|
57
60
|
|
|
58
61
|
Card.Title = CardTitle;
|
|
59
62
|
Card.Body = CardBody;
|