@pautena/react-design-system 0.3.0 → 0.4.1

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.
Files changed (45) hide show
  1. package/dist/cjs/index.js +5 -5
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/types/components/enhanced-select/enhanced-select.d.ts +16 -0
  4. package/dist/cjs/types/components/enhanced-select/index.d.ts +1 -0
  5. package/dist/cjs/types/components/index.d.ts +1 -0
  6. package/dist/cjs/types/generators/model-router/model-router.types.d.ts +1 -0
  7. package/dist/cjs/types/generators/model-router/screens/details-screen.d.ts +1 -1
  8. package/dist/cjs/types/hooks/index.d.ts +1 -0
  9. package/dist/cjs/types/hooks/routing/index.d.ts +1 -0
  10. package/dist/cjs/types/hooks/routing/routing.hooks.d.ts +5 -0
  11. package/dist/cjs/types/providers/notification-center/index.d.ts +1 -0
  12. package/dist/cjs/types/providers/notification-center/notification-center.hooks.d.ts +6 -0
  13. package/dist/esm/index.js +5 -5
  14. package/dist/esm/index.js.map +1 -1
  15. package/dist/esm/types/components/enhanced-select/enhanced-select.d.ts +16 -0
  16. package/dist/esm/types/components/enhanced-select/index.d.ts +1 -0
  17. package/dist/esm/types/components/index.d.ts +1 -0
  18. package/dist/esm/types/generators/model-router/model-router.types.d.ts +1 -0
  19. package/dist/esm/types/generators/model-router/screens/details-screen.d.ts +1 -1
  20. package/dist/esm/types/hooks/index.d.ts +1 -0
  21. package/dist/esm/types/hooks/routing/index.d.ts +1 -0
  22. package/dist/esm/types/hooks/routing/routing.hooks.d.ts +5 -0
  23. package/dist/esm/types/providers/notification-center/index.d.ts +1 -0
  24. package/dist/esm/types/providers/notification-center/notification-center.hooks.d.ts +6 -0
  25. package/dist/index.d.ts +23 -1
  26. package/package.json +1 -1
  27. package/src/components/enhanced-select/enhanced-select.stories.tsx +99 -0
  28. package/src/components/enhanced-select/enhanced-select.test.tsx +90 -0
  29. package/src/components/enhanced-select/enhanced-select.tsx +110 -0
  30. package/src/components/enhanced-select/index.ts +1 -0
  31. package/src/components/index.ts +1 -0
  32. package/src/generators/model-router/model-router.test.tsx +246 -20
  33. package/src/generators/model-router/model-router.types.ts +4 -0
  34. package/src/generators/model-router/screens/add-screen.tsx +16 -20
  35. package/src/generators/model-router/screens/details-screen.tsx +3 -2
  36. package/src/generators/model-router/screens/list-screen.tsx +17 -0
  37. package/src/generators/model-router/screens/update-screen.tsx +22 -13
  38. package/src/hooks/index.ts +1 -0
  39. package/src/hooks/routing/index.ts +1 -0
  40. package/src/hooks/routing/routing.hooks.ts +23 -0
  41. package/src/hooks/routing/routing.test.tsx +83 -0
  42. package/src/providers/notification-center/index.ts +1 -0
  43. package/src/providers/notification-center/notification-center.hooks.ts +23 -0
  44. package/src/providers/notification-center/notification-center.test.tsx +87 -1
  45. package/src/tests/assertions.ts +5 -0
@@ -0,0 +1,16 @@
1
+ import React, { ReactNode } from "react";
2
+ import { SelectInputProps } from "@mui/material/Select/SelectInput";
3
+ declare type EnhancedSelectSize = "small" | "medium";
4
+ export interface EnhancedSelectProps<T> {
5
+ label: string;
6
+ value: T;
7
+ loading?: boolean;
8
+ fetching?: boolean;
9
+ size?: EnhancedSelectSize;
10
+ color?: string;
11
+ fullWidth?: boolean;
12
+ children?: ReactNode;
13
+ onChange?: SelectInputProps<T>["onChange"];
14
+ }
15
+ export declare const EnhancedSelect: <T extends React.ReactNode>({ label, value, loading, fetching, size, fullWidth, color, children, onChange, }: EnhancedSelectProps<T>) => JSX.Element;
16
+ export {};
@@ -0,0 +1 @@
1
+ export * from "./enhanced-select";
@@ -16,3 +16,4 @@ export * from "./drawer-item";
16
16
  export * from "./center-container";
17
17
  export * from "./value-displays";
18
18
  export * from "./content";
19
+ export * from "./enhanced-select";
@@ -6,3 +6,4 @@ export interface RequestState {
6
6
  }
7
7
  export declare const IdleRequest: RequestState;
8
8
  export declare const LoadingRequest: RequestState;
9
+ export declare const SuccessRequest: RequestState;
@@ -17,4 +17,4 @@ export interface DetailsScreenProps<T extends BasicModelInstance> extends BaseSc
17
17
  */
18
18
  detailsItem?: T;
19
19
  }
20
- export declare const DetailsScreen: <T extends BasicModelInstance>({ model, modelName, onRequestItem, itemRequest, detailsItem, }: DetailsScreenProps<T>) => JSX.Element;
20
+ export declare const DetailsScreen: <T extends BasicModelInstance>({ model, modelName, basePath, onRequestItem, itemRequest, detailsItem, }: DetailsScreenProps<T>) => JSX.Element;
@@ -0,0 +1 @@
1
+ export * from "./routing";
@@ -0,0 +1 @@
1
+ export * from "./routing.hooks";
@@ -0,0 +1,5 @@
1
+ export interface NavigateWhenValueChangesOptions<T> {
2
+ from: T;
3
+ to: T;
4
+ }
5
+ export declare const useNavigateWhenValueChanges: <T>(path: string, value: T | undefined, { from, to }: NavigateWhenValueChangesOptions<T>) => void;
@@ -1,2 +1,3 @@
1
1
  export * from "./notification-center.provider";
2
2
  export * from "./notification-center.context";
3
+ export * from "./notification-center.hooks";
@@ -0,0 +1,6 @@
1
+ import { Notification } from "./notification-center.context";
2
+ export interface NotifyWhenValueChangesOptions<T> {
3
+ from: T;
4
+ to: T;
5
+ }
6
+ export declare const useNotifyWhenValueChanges: <T>(notification: Notification, value: T | undefined, { from, to }: NotifyWhenValueChangesOptions<T>) => void;
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ import { LinkProps } from 'react-router-dom';
8
8
  import * as _emotion_styled from '@emotion/styled';
9
9
  import * as _mui_system from '@mui/system';
10
10
  import { BasicModelInstance as BasicModelInstance$1 } from '~/generators';
11
+ import { SelectInputProps } from '@mui/material/Select/SelectInput';
11
12
 
12
13
  declare type HeaderPreset = PropTypes.Color | "transparent";
13
14
  declare type HeaderActionVariant = "text" | "outlined" | "contained";
@@ -457,6 +458,20 @@ declare type ContentElement = ReactElement<ContentElement, ContentComponent>;
457
458
 
458
459
  declare const Content: ({ children }: ContentProps) => JSX.Element;
459
460
 
461
+ declare type EnhancedSelectSize = "small" | "medium";
462
+ interface EnhancedSelectProps<T> {
463
+ label: string;
464
+ value: T;
465
+ loading?: boolean;
466
+ fetching?: boolean;
467
+ size?: EnhancedSelectSize;
468
+ color?: string;
469
+ fullWidth?: boolean;
470
+ children?: ReactNode;
471
+ onChange?: SelectInputProps<T>["onChange"];
472
+ }
473
+ declare const EnhancedSelect: <T extends React__default.ReactNode>({ label, value, loading, fetching, size, fullWidth, color, children, onChange, }: EnhancedSelectProps<T>) => JSX.Element;
474
+
460
475
  declare const newArrayWithSize: <T>(size: number, fillValue: T) => any[];
461
476
  declare const getRandomItem: <T>(items: T[]) => {
462
477
  index: number;
@@ -554,6 +569,7 @@ interface RequestState {
554
569
  }
555
570
  declare const IdleRequest: RequestState;
556
571
  declare const LoadingRequest: RequestState;
572
+ declare const SuccessRequest: RequestState;
557
573
 
558
574
  interface BaseScreenProps {
559
575
  /**
@@ -700,6 +716,12 @@ interface NotificationCenterProps {
700
716
  declare const NotificationCenterContext: React__default.Context<NotificationCenterProps | undefined>;
701
717
  declare const useNotificationCenter: () => NotificationCenterProps;
702
718
 
719
+ interface NotifyWhenValueChangesOptions<T> {
720
+ from: T;
721
+ to: T;
722
+ }
723
+ declare const useNotifyWhenValueChanges: <T>(notification: Notification, value: T | undefined, { from, to }: NotifyWhenValueChangesOptions<T>) => void;
724
+
703
725
  declare const TabContext: React.Context<[number, Dispatch<SetStateAction<number>>]>;
704
726
  declare const TabContextProvider: React.Provider<[number, Dispatch<SetStateAction<number>>]>;
705
727
  declare const useTab: () => [number, Dispatch<SetStateAction<number>>];
@@ -709,4 +731,4 @@ declare type TabProviderProps = PropsWithChildren<{
709
731
  }>;
710
732
  declare const TabProvider: ({ children, initialValue }: TabProviderProps) => JSX.Element;
711
733
 
712
- export { AppBarComponent, AppBarElement, AppBarProfile, AppBarProps, AppBarWithDrawerLayout, AppBarWithDrawerLayoutProps, BasicModelInstance, Bullet, BulletProps, BulletVariant, CenterContainer, CenterContainerProps, Content, ContentComponent, ContentElement, ContentProps, Drawer, DrawerComponent, DrawerContent, DrawerContentComponent, DrawerContentElement, DrawerContentProps, DrawerContext, DrawerContextProps, DrawerElement, DrawerItem, DrawerItemProps, DrawerProps, DrawerProvider, DrawerSection, DrawerSectionProps, EnhancedRemoteTable, EnhancedTable, EnhancedTableHead, GroupField, GroupValueCard, GroupValueCardProps, GroupValueItem, GroupValueItemComponent, GroupValueItemElement, GroupValueItemProps, HeadCell, Header, HeaderAction, HeaderActionVariant, HeaderBreadcrumb, HeaderComponent, HeaderElement, HeaderLayout, HeaderPreset, HeaderProps, HeaderTab, IdleRequest, Label, LabelProps, LabelVariant, Link, LinkBehaviour, LoadingRequest, MiniAppBar, MiniDrawer, Model, ModelField, ModelFieldTypes, ModelForm, ModelFormProps, ModelRouter, ModelRouterProps, Nav, NavItem, NavItemAvatar, NavItemBullet, NavItemLabel, NavSection, Notification, NotificationCenterContext, NotificationCenterProps, NotificationCenterProvider, NotificationCenterProviderProps, NotificationCenterProviderUndefinedError, ObjectDetails, ObjectDetailsProps, Order, Placeholder, PlaceholderAction, PlaceholderIconArgs, PlaceholderProps, QueryContainer, QueryContainerError, QueryContainerProps, QueryContainerSuccess, RequestState, SignIn, SnackbarActionType, SnackbarContentType, TabCard, TabContext, TabContextProvider, TabPanel, TabProvider, TableList, TableListProps, TableRowOption, UndefinedProvider, ValueBoolean, ValueBooleanProps, ValueCard, ValueCardProps, ValueDatetime, ValueDatetimeProps, ValueText, ValueTextProps, bulletClasses, getRandomItem, groupValueItemClasses, labelClasses, newArrayWithSize, useDrawer, useGetDefaultThemeColor, useNotificationCenter, useTab };
734
+ export { AppBarComponent, AppBarElement, AppBarProfile, AppBarProps, AppBarWithDrawerLayout, AppBarWithDrawerLayoutProps, BasicModelInstance, Bullet, BulletProps, BulletVariant, CenterContainer, CenterContainerProps, Content, ContentComponent, ContentElement, ContentProps, Drawer, DrawerComponent, DrawerContent, DrawerContentComponent, DrawerContentElement, DrawerContentProps, DrawerContext, DrawerContextProps, DrawerElement, DrawerItem, DrawerItemProps, DrawerProps, DrawerProvider, DrawerSection, DrawerSectionProps, EnhancedRemoteTable, EnhancedSelect, EnhancedSelectProps, EnhancedTable, EnhancedTableHead, GroupField, GroupValueCard, GroupValueCardProps, GroupValueItem, GroupValueItemComponent, GroupValueItemElement, GroupValueItemProps, HeadCell, Header, HeaderAction, HeaderActionVariant, HeaderBreadcrumb, HeaderComponent, HeaderElement, HeaderLayout, HeaderPreset, HeaderProps, HeaderTab, IdleRequest, Label, LabelProps, LabelVariant, Link, LinkBehaviour, LoadingRequest, MiniAppBar, MiniDrawer, Model, ModelField, ModelFieldTypes, ModelForm, ModelFormProps, ModelRouter, ModelRouterProps, Nav, NavItem, NavItemAvatar, NavItemBullet, NavItemLabel, NavSection, Notification, NotificationCenterContext, NotificationCenterProps, NotificationCenterProvider, NotificationCenterProviderProps, NotificationCenterProviderUndefinedError, NotifyWhenValueChangesOptions, ObjectDetails, ObjectDetailsProps, Order, Placeholder, PlaceholderAction, PlaceholderIconArgs, PlaceholderProps, QueryContainer, QueryContainerError, QueryContainerProps, QueryContainerSuccess, RequestState, SignIn, SnackbarActionType, SnackbarContentType, SuccessRequest, TabCard, TabContext, TabContextProvider, TabPanel, TabProvider, TableList, TableListProps, TableRowOption, UndefinedProvider, ValueBoolean, ValueBooleanProps, ValueCard, ValueCardProps, ValueDatetime, ValueDatetimeProps, ValueText, ValueTextProps, bulletClasses, getRandomItem, groupValueItemClasses, labelClasses, newArrayWithSize, useDrawer, useGetDefaultThemeColor, useNotificationCenter, useNotifyWhenValueChanges, useTab };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pautena/react-design-system",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "My custom design system on top of MUI",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -0,0 +1,99 @@
1
+ import { Box, MenuItem, PropTypes, useTheme } from "@mui/material";
2
+ import { ComponentMeta } from "@storybook/react";
3
+ import React, { ReactNode } from "react";
4
+ import { createTemplate, withContainer } from "../../storybook";
5
+ import { EnhancedSelect, EnhancedSelectProps } from "./enhanced-select";
6
+ import { faker } from "@faker-js/faker";
7
+
8
+ const baseArgs = {
9
+ label: "Car model",
10
+ value: faker.vehicle.model(),
11
+ size: "medium",
12
+ fetching: false,
13
+ loading: false,
14
+ fullWidth: true,
15
+ options: faker.definitions.vehicle?.model || [],
16
+ };
17
+
18
+ export default {
19
+ title: "Input/EnhancedSelect",
20
+ component: EnhancedSelect,
21
+ decorators: [withContainer({ width: 200 })],
22
+ parameters: {
23
+ layout: "centered",
24
+ },
25
+ } as ComponentMeta<typeof EnhancedSelect>;
26
+
27
+ interface TemplateProps<T extends ReactNode> extends EnhancedSelectProps<T> {
28
+ options: T[];
29
+ }
30
+
31
+ const Template = createTemplate(<T extends string>({ options, ...rest }: TemplateProps<T>) => {
32
+ return (
33
+ <EnhancedSelect {...rest}>
34
+ {options.map((option) => (
35
+ <MenuItem key={option} value={option}>
36
+ {option}
37
+ </MenuItem>
38
+ ))}
39
+ </EnhancedSelect>
40
+ );
41
+ });
42
+
43
+ export const WithoutFullWidth = Template.bind({});
44
+ WithoutFullWidth.args = {
45
+ ...baseArgs,
46
+ fullWidth: false,
47
+ };
48
+
49
+ export const EnhancedSelectLoaded = Template.bind({});
50
+ EnhancedSelectLoaded.args = {
51
+ ...baseArgs,
52
+ };
53
+
54
+ export const Loading = Template.bind({});
55
+ Loading.args = {
56
+ ...baseArgs,
57
+ loading: true,
58
+ };
59
+
60
+ export const Fetching = Template.bind({});
61
+ Fetching.args = {
62
+ ...baseArgs,
63
+ fetching: true,
64
+ };
65
+
66
+ export const SizeSmall = Template.bind({});
67
+ SizeSmall.args = {
68
+ ...baseArgs,
69
+ size: "small",
70
+ };
71
+
72
+ type WithBackgroundProps<T extends ReactNode> = TemplateProps<T> & { bgcolor: PropTypes.Color };
73
+
74
+ export const WithBackground = <T extends string>({
75
+ options,
76
+ bgcolor: bgcolorProp,
77
+ ...rest
78
+ }: WithBackgroundProps<T>) => {
79
+ const { palette } = useTheme();
80
+ const bgcolor = palette[bgcolorProp].main;
81
+ const selectColor = palette.getContrastText(bgcolor);
82
+
83
+ return (
84
+ <Box bgcolor={bgcolor} padding={3}>
85
+ <EnhancedSelect {...rest} color={selectColor}>
86
+ {options.map((option) => (
87
+ <MenuItem key={option} value={option}>
88
+ {option}
89
+ </MenuItem>
90
+ ))}
91
+ </EnhancedSelect>
92
+ </Box>
93
+ );
94
+ };
95
+
96
+ WithBackground.args = {
97
+ bgcolor: "secondary",
98
+ ...baseArgs,
99
+ };
@@ -0,0 +1,90 @@
1
+ import React from "react";
2
+ import { EnhancedSelectLoaded } from "./enhanced-select.stories";
3
+ import { render, screen } from "../../tests";
4
+ import userEvent from "@testing-library/user-event";
5
+
6
+ describe("EnhancedSelect", () => {
7
+ const renderComponent = ({
8
+ label = EnhancedSelectLoaded.args.label,
9
+ loading = false,
10
+ fetching = false,
11
+ } = {}) => {
12
+ render(
13
+ <EnhancedSelectLoaded
14
+ {...EnhancedSelectLoaded.args}
15
+ label={label}
16
+ loading={loading}
17
+ fetching={fetching}
18
+ />,
19
+ );
20
+
21
+ return {
22
+ options: EnhancedSelectLoaded.args.options as string[],
23
+ value: EnhancedSelectLoaded.args.value as string,
24
+ };
25
+ };
26
+
27
+ it("would render a select with a label", () => {
28
+ renderComponent({ label: "Lorem ipsum" });
29
+
30
+ expect(screen.getByRole("button", { name: /lorem ipsum/i })).toBeInTheDocument();
31
+ });
32
+
33
+ it("would render the value", () => {
34
+ const { value } = renderComponent();
35
+
36
+ expect(screen.getByText(value)).toBeInTheDocument();
37
+ });
38
+
39
+ describe("when is loading", () => {
40
+ it("would render a progress indicator", () => {
41
+ renderComponent({ loading: true });
42
+
43
+ expect(screen.getByRole("progressbar")).toBeInTheDocument();
44
+ });
45
+
46
+ it("would render the value", () => {
47
+ const { value } = renderComponent({ loading: true });
48
+
49
+ expect(screen.getByText(value)).toBeInTheDocument();
50
+ });
51
+ });
52
+
53
+ describe("when is fetching", () => {
54
+ it("would render a progress indicator", () => {
55
+ renderComponent({ fetching: true });
56
+
57
+ expect(screen.getByRole("progressbar")).toBeInTheDocument();
58
+ });
59
+
60
+ it("wouldn't render the value", () => {
61
+ const { value } = renderComponent({ fetching: true });
62
+
63
+ expect(screen.queryByText(value)).not.toBeInTheDocument();
64
+ });
65
+ });
66
+
67
+ describe("when is fetching and loading", () => {
68
+ it("would render a progress indicator", () => {
69
+ renderComponent({ fetching: true, loading: true });
70
+
71
+ expect(screen.getByRole("progressbar")).toBeInTheDocument();
72
+ });
73
+
74
+ it("wouldn't render the value", () => {
75
+ const { value } = renderComponent({ fetching: true, loading: true });
76
+
77
+ expect(screen.queryByText(value)).not.toBeInTheDocument();
78
+ });
79
+ });
80
+
81
+ it("would render a menu item for each option", async () => {
82
+ const { options } = renderComponent({ label: "Lorem ipsum" });
83
+
84
+ await userEvent.click(screen.getByRole("button", { name: /lorem ipsum/i }));
85
+
86
+ options.forEach((option) => {
87
+ expect(screen.getByRole("option", { name: option })).toBeInTheDocument();
88
+ });
89
+ });
90
+ });
@@ -0,0 +1,110 @@
1
+ import React, { ReactNode, useId } from "react";
2
+ import {
3
+ Box,
4
+ CircularProgress,
5
+ FormControl,
6
+ InputLabel,
7
+ LinearProgress,
8
+ useTheme,
9
+ Select,
10
+ Typography,
11
+ styled,
12
+ } from "@mui/material";
13
+ import { CenterContainer } from "../center-container";
14
+ import { SelectInputProps } from "@mui/material/Select/SelectInput";
15
+
16
+ type EnhancedSelectSize = "small" | "medium";
17
+
18
+ export interface EnhancedSelectProps<T> {
19
+ label: string;
20
+ value: T;
21
+ loading?: boolean;
22
+ fetching?: boolean;
23
+ size?: EnhancedSelectSize;
24
+ color?: string;
25
+ fullWidth?: boolean;
26
+ children?: ReactNode;
27
+ onChange?: SelectInputProps<T>["onChange"];
28
+ }
29
+
30
+ const ProgressSize: Record<EnhancedSelectSize, number> = {
31
+ small: 15,
32
+ medium: 20,
33
+ };
34
+
35
+ export const EnhancedSelect = <T extends ReactNode>({
36
+ label,
37
+ value,
38
+ loading = false,
39
+ fetching = false,
40
+ size = "medium",
41
+ fullWidth = false,
42
+ color,
43
+ children,
44
+ onChange,
45
+ }: EnhancedSelectProps<T>) => {
46
+ const id = useId();
47
+
48
+ const renderValue = (value: T): ReactNode => {
49
+ if (fetching) {
50
+ return (
51
+ <CenterContainer centerVertical centerHorizontal>
52
+ <CircularProgress color="inherit" size={ProgressSize[size]} />
53
+ </CenterContainer>
54
+ );
55
+ }
56
+
57
+ if (loading) {
58
+ return (
59
+ <Box display="flex" flexDirection="column">
60
+ {value}
61
+ <LinearProgress
62
+ color="inherit"
63
+ sx={{ position: "absolute", left: 0, right: 0, bottom: 0 }}
64
+ />
65
+ </Box>
66
+ );
67
+ }
68
+
69
+ return value;
70
+ };
71
+
72
+ const StyledFormControl = styled(FormControl)(() => {
73
+ if (!color) {
74
+ return {};
75
+ }
76
+
77
+ return {
78
+ label: {
79
+ color,
80
+ },
81
+ ".MuiOutlinedInput-notchedOutline": {
82
+ borderColor: `${color} !important`,
83
+ },
84
+ ".MuiInputBase-root": {
85
+ color,
86
+ },
87
+ ".MuiSelect-icon": {
88
+ fill: color,
89
+ },
90
+ };
91
+ });
92
+
93
+ return (
94
+ <StyledFormControl fullWidth={fullWidth}>
95
+ <InputLabel id={id}>{label}</InputLabel>
96
+ <Select
97
+ labelId={id}
98
+ id={id}
99
+ value={value}
100
+ label={label}
101
+ onChange={onChange}
102
+ disabled={fetching}
103
+ size={size}
104
+ renderValue={renderValue}
105
+ >
106
+ {children}
107
+ </Select>
108
+ </StyledFormControl>
109
+ );
110
+ };
@@ -0,0 +1 @@
1
+ export * from "./enhanced-select";
@@ -16,3 +16,4 @@ export * from "./drawer-item";
16
16
  export * from "./center-container";
17
17
  export * from "./value-displays";
18
18
  export * from "./content";
19
+ export * from "./enhanced-select";