@ssa-ui-kit/core 1.0.1 → 1.0.3

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 (65) hide show
  1. package/.storybook/style.css +9 -0
  2. package/dist/components/Button/fixtures.d.ts +8 -0
  3. package/dist/components/Button/types.d.ts +2 -0
  4. package/dist/components/FormHelperText/FormHelperText.d.ts +1 -1
  5. package/dist/components/Input/types.d.ts +1 -0
  6. package/dist/components/Label/Label.d.ts +1 -1
  7. package/dist/components/Label/LabelBase.d.ts +2 -0
  8. package/dist/components/Label/types.d.ts +1 -0
  9. package/dist/components/Typeahead/Typeahead.context.d.ts +36 -0
  10. package/dist/components/Typeahead/Typeahead.d.ts +11 -0
  11. package/dist/components/Typeahead/components/MultipleTrigger.d.ts +1 -0
  12. package/dist/components/Typeahead/components/NoOptions.d.ts +1 -0
  13. package/dist/components/Typeahead/components/SingleTrigger.d.ts +1 -0
  14. package/dist/components/Typeahead/components/TypeaheadItem.d.ts +8 -0
  15. package/dist/components/Typeahead/components/TypeaheadOption.d.ts +2 -0
  16. package/dist/components/Typeahead/components/TypeaheadOptions.d.ts +2 -0
  17. package/dist/components/Typeahead/components/TypeaheadTrigger.d.ts +1 -0
  18. package/dist/components/Typeahead/components/index.d.ts +7 -0
  19. package/dist/components/Typeahead/index.d.ts +5 -0
  20. package/dist/components/Typeahead/styles.d.ts +47 -0
  21. package/dist/components/Typeahead/types.d.ts +46 -0
  22. package/dist/components/Typeahead/useTypeahead.d.ts +36 -0
  23. package/dist/components/Typeahead/utils.d.ts +1 -0
  24. package/dist/components/index.d.ts +1 -0
  25. package/dist/index.js +1 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/types/emotion.d.ts +4 -1
  28. package/package.json +1 -1
  29. package/src/components/Button/Button.tsx +10 -2
  30. package/src/components/Button/types.ts +2 -0
  31. package/src/components/CollapsibleNavBar/stories/CollapsibleNavBar.stories.tsx +47 -25
  32. package/src/components/CollapsibleNavBar/stories/Layout.tsx +1 -1
  33. package/src/components/Filters/Filters.tsx +1 -1
  34. package/src/components/FormHelperText/FormHelperText.tsx +2 -1
  35. package/src/components/Input/Input.spec.tsx +6 -8
  36. package/src/components/Input/Input.tsx +14 -8
  37. package/src/components/Input/types.ts +1 -0
  38. package/src/components/Label/Label.tsx +2 -0
  39. package/src/components/Label/LabelBase.tsx +3 -2
  40. package/src/components/Label/types.ts +1 -0
  41. package/src/components/MultipleDropdownOptions/MultipleDropdownOptions.tsx +1 -1
  42. package/src/components/ResponsiveImage/ResponsiveImage.stories.tsx +6 -4
  43. package/src/components/TableFilters/styles.ts +1 -0
  44. package/src/components/TextField/TextField.stories.tsx +8 -1
  45. package/src/components/Typeahead/Typeahead.context.ts +59 -0
  46. package/src/components/Typeahead/Typeahead.spec.tsx +506 -0
  47. package/src/components/Typeahead/Typeahead.stories.tsx +372 -0
  48. package/src/components/Typeahead/Typeahead.tsx +120 -0
  49. package/src/components/Typeahead/components/MultipleTrigger.tsx +116 -0
  50. package/src/components/Typeahead/components/NoOptions.tsx +7 -0
  51. package/src/components/Typeahead/components/SingleTrigger.tsx +71 -0
  52. package/src/components/Typeahead/components/TypeaheadItem.ts +14 -0
  53. package/src/components/Typeahead/components/TypeaheadOption.tsx +12 -0
  54. package/src/components/Typeahead/components/TypeaheadOptions.tsx +25 -0
  55. package/src/components/Typeahead/components/TypeaheadTrigger.tsx +26 -0
  56. package/src/components/Typeahead/components/index.ts +7 -0
  57. package/src/components/Typeahead/index.ts +5 -0
  58. package/src/components/Typeahead/styles.ts +193 -0
  59. package/src/components/Typeahead/types.ts +77 -0
  60. package/src/components/Typeahead/useTypeahead.tsx +321 -0
  61. package/src/components/Typeahead/utils.tsx +22 -0
  62. package/src/components/index.ts +1 -0
  63. package/src/themes/main.ts +3 -0
  64. package/src/types/emotion.ts +3 -0
  65. package/tsbuildcache +1 -1
@@ -12,6 +12,7 @@ type Colors = MakeColors<[
12
12
  'white30',
13
13
  'dark',
14
14
  'greyLighter',
15
+ 'greyLighter40',
15
16
  'greySelectedMenuItem',
16
17
  'greyFocused',
17
18
  'greyFocused40',
@@ -20,6 +21,7 @@ type Colors = MakeColors<[
20
21
  'grey40',
21
22
  'greyShadow24',
22
23
  'greyDarker',
24
+ 'greyDarker40',
23
25
  'greyDarker60',
24
26
  'greyDarker80',
25
27
  'greyDarker14',
@@ -105,7 +107,8 @@ type Colors = MakeColors<[
105
107
  'blueButtonHoverGradientFrom',
106
108
  'blueButtonHoverGradientTo',
107
109
  'blueButtonActive',
108
- 'blueRoyal'
110
+ 'blueRoyal',
111
+ 'blueRoyal12'
109
112
  ]>;
110
113
  export type ColorsKeys = keyof Colors;
111
114
  type MediaQueryString = `@media${string}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ssa-ui-kit/core",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "private": false,
@@ -47,6 +47,8 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
47
47
  text,
48
48
  startIcon,
49
49
  endIcon,
50
+ startIconClassName,
51
+ endIconClassName,
50
52
  variant = 'primary',
51
53
  type = 'button',
52
54
  className,
@@ -89,7 +91,10 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
89
91
  onClick={onClick}
90
92
  {...ariaProps}>
91
93
  {startIcon ? (
92
- <span style={noMargin} css={[iconWrapperRight]}>
94
+ <span
95
+ style={noMargin}
96
+ css={[iconWrapperRight]}
97
+ className={startIconClassName}>
93
98
  {startIcon}
94
99
  </span>
95
100
  ) : null}
@@ -107,7 +112,10 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
107
112
  )
108
113
  ) : null}
109
114
  {endIcon ? (
110
- <span style={noMargin} css={iconWrapperLeft}>
115
+ <span
116
+ style={noMargin}
117
+ css={[iconWrapperLeft]}
118
+ className={endIconClassName}>
111
119
  {endIcon}
112
120
  </span>
113
121
  ) : null}
@@ -21,6 +21,8 @@ export interface ButtonProps extends ButtonAriaProps {
21
21
  text?: string;
22
22
  startIcon?: React.ReactNode;
23
23
  endIcon?: React.ReactNode;
24
+ startIconClassName?: string;
25
+ endIconClassName?: string;
24
26
  variant?: keyof ButtonVariants | 'custom';
25
27
  type?: 'button' | 'reset' | 'submit';
26
28
  isDisabled?: boolean;
@@ -1,7 +1,7 @@
1
1
  import { Fragment } from 'react';
2
2
  import { Routes, Route, MemoryRouter } from 'react-router-dom';
3
- import { Meta } from '@storybook/react';
4
- import { Title, Description, Source } from '@storybook/addon-docs';
3
+ import { Meta, StoryObj } from '@storybook/react';
4
+ import { Title, Description, Subtitle, Stories } from '@storybook/addon-docs';
5
5
 
6
6
  import { DecoratorFunction } from '@storybook/types';
7
7
  import { CollapsibleNavBar } from '../CollapsibleNavBar';
@@ -35,11 +35,17 @@ export default {
35
35
  parameters: {
36
36
  layout: 'fullscreen',
37
37
  docs: {
38
+ source: {
39
+ type: 'code',
40
+ },
38
41
  page: () => (
39
42
  <Fragment>
40
43
  <Title />
44
+ <Subtitle />
41
45
  <Description />
42
- <Source code={`<CollapsibleNavBar />`} />
46
+ <div css={{ height: 300 }}>
47
+ <Stories />
48
+ </div>
43
49
  </Fragment>
44
50
  ),
45
51
  },
@@ -54,26 +60,42 @@ export default {
54
60
  },
55
61
  } as Meta<typeof CollapsibleNavBar>;
56
62
 
57
- export const Default = {};
63
+ export const Default: StoryObj<typeof CollapsibleNavBar> = () => {
64
+ return (
65
+ <CollapsibleNavBar
66
+ items={ITEMS}
67
+ renderLogo={<Logo />}
68
+ onChange={(isChecked) => {
69
+ console.log('>>>onChange', isChecked);
70
+ }}
71
+ />
72
+ );
73
+ };
58
74
 
59
- export const WithCustomIcon = {
60
- title: 'With Custom Icon',
61
- component: CollapsibleNavBar,
62
- parameters: {
63
- layout: 'fullscreen',
64
- },
65
- args: {
66
- items: [
67
- ...ITEMS,
68
- {
69
- path: 'custom',
70
- CustomIcon,
71
- title: 'Item with custom icon',
72
- },
73
- ],
74
- renderLogo: <Logo />,
75
- onChange: (isChecked) => {
76
- console.log('>>>onChange', isChecked);
77
- },
78
- },
79
- } as Meta<typeof CollapsibleNavBar>;
75
+ Default.args = {};
76
+
77
+ export const WithCustomIcon: Meta<typeof CollapsibleNavBar> = () => {
78
+ return (
79
+ <CollapsibleNavBar
80
+ items={[
81
+ ...ITEMS,
82
+ {
83
+ path: 'custom',
84
+ CustomIcon,
85
+ title: 'Item with custom icon',
86
+ iconSize: 22,
87
+ iconName: 'archive',
88
+ },
89
+ ]}
90
+ renderLogo={<Logo />}
91
+ onChange={(isChecked) => {
92
+ console.log('>>>onChange', isChecked);
93
+ }}
94
+ />
95
+ );
96
+ };
97
+
98
+ WithCustomIcon.title = 'With Custom Icon';
99
+ WithCustomIcon.parameters = {
100
+ layout: 'fullscreen',
101
+ };
@@ -36,7 +36,7 @@ export const Layout = ({ children }: { children: React.ReactNode }) => {
36
36
  }}>
37
37
  {children}
38
38
  <Main>
39
- {new Array(100).fill(1).map((_, index) => (
39
+ {new Array(30).fill(1).map((_, index) => (
40
40
  <NotificationCard
41
41
  key={index}
42
42
  title="CyberVeinToken is Now Available"
@@ -74,7 +74,7 @@ export const Filters = ({
74
74
  <FilterBlockWrapper>
75
75
  <FiltersBlock />
76
76
  </FilterBlockWrapper>
77
- <Wrapper css={{ width: 110 }}>
77
+ <Wrapper css={{ width: 110, zIndex: 1 }}>
78
78
  <TableFilters
79
79
  {...useTableDataResult}
80
80
  checkboxData={hiddenCheckboxData}
@@ -6,10 +6,11 @@ const FormHelperText = ({
6
6
  status,
7
7
  disabled,
8
8
  children,
9
+ ...rest
9
10
  }: FormHelperTextProps) => {
10
11
  status = disabled ? 'basic' : status;
11
12
  return (
12
- <FormHelperTextBase role={role} status={status}>
13
+ <FormHelperTextBase role={role} status={status} {...rest}>
13
14
  {children}
14
15
  </FormHelperTextBase>
15
16
  );
@@ -15,14 +15,12 @@ describe('Inputs', () => {
15
15
  expect(input).toBeInTheDocument();
16
16
  });
17
17
 
18
- it('Trow error when without register', () => {
19
- jest.spyOn(console, 'error').mockImplementation();
20
-
21
- expect(() =>
22
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
23
- // @ts-ignore
24
- render(<Input placeholder="Field" name="field" disabled={true} />),
25
- ).toThrow('Input component must be used within a Form component');
18
+ it('Throw warning when without register', () => {
19
+ const warnSpy = jest.spyOn(console, 'warn').mockImplementation();
20
+ render(<Input placeholder="Field" name="field" disabled={true} />);
21
+ expect(warnSpy).toBeCalledWith(
22
+ 'Input component must be used within a Form component',
23
+ );
26
24
  });
27
25
 
28
26
  it('Render input disabled', () => {
@@ -1,4 +1,4 @@
1
- import { forwardRef } from 'react';
1
+ import { forwardRef, useEffect } from 'react';
2
2
  import { useMergeRefs } from '@floating-ui/react';
3
3
  import { InputBase } from './InputBase';
4
4
  import { InputGroup } from './InputGroup';
@@ -25,21 +25,27 @@ const InputInner = (
25
25
  startElement,
26
26
  endElement,
27
27
  className,
28
+ wrapperClassName,
28
29
  inputProps = {},
29
30
  register,
30
31
  onKeyUp,
31
32
  }: InputProps,
32
33
  inputRef?: React.ForwardedRef<HTMLInputElement | null>,
33
34
  ) => {
34
- if (!register) {
35
- throw new Error('Input component must be used within a Form component');
36
- }
35
+ useEffect(() => {
36
+ if (!register) {
37
+ console.warn('Input component must be used within a Form component');
38
+ }
39
+ }, []);
37
40
 
38
41
  const showStatusIcon = () => !disabled && !endElement;
39
42
 
40
- const registerResult = register(name, validationSchema);
43
+ const registerResult = register?.(name, validationSchema);
41
44
  return (
42
- <InputGroup css={[mapColors[status]]} disabled={disabled}>
45
+ <InputGroup
46
+ css={[mapColors[status]]}
47
+ className={wrapperClassName}
48
+ disabled={disabled}>
43
49
  {startElement ? <div css={S.startElement}>{startElement}</div> : null}
44
50
  <InputBase
45
51
  type={type}
@@ -53,8 +59,8 @@ const InputInner = (
53
59
  className={className}
54
60
  onKeyUp={onKeyUp}
55
61
  {...inputProps}
56
- {...register(name, validationSchema)}
57
- ref={useMergeRefs([registerResult.ref, inputRef])}
62
+ {...register?.(name, validationSchema)}
63
+ ref={useMergeRefs([registerResult?.ref, inputRef])}
58
64
  />
59
65
 
60
66
  {status === 'error' && showStatusIcon() ? <InputStatusError /> : null}
@@ -16,6 +16,7 @@ export interface InputProps
16
16
  validationSchema?: Record<string, unknown>;
17
17
  disabled?: boolean;
18
18
  className?: string;
19
+ wrapperClassName?: string;
19
20
  as?: React.ElementType;
20
21
  startElement?: React.ReactElement;
21
22
  endElement?: React.ReactElement;
@@ -5,11 +5,13 @@ const Label = ({
5
5
  children,
6
6
  htmlFor,
7
7
  className,
8
+ isDisabled,
8
9
  onMouseEnter,
9
10
  onMouseLeave,
10
11
  }: LabelProps) => (
11
12
  <LabelBase
12
13
  htmlFor={htmlFor}
14
+ isDisabled={isDisabled}
13
15
  className={className}
14
16
  onMouseEnter={onMouseEnter}
15
17
  onMouseLeave={onMouseLeave}>
@@ -1,6 +1,6 @@
1
1
  import styled from '@emotion/styled';
2
2
 
3
- export const LabelBase = styled.label`
3
+ export const LabelBase = styled.label<{ isDisabled?: boolean }>`
4
4
  display: inline-block;
5
5
 
6
6
  flex: 1;
@@ -8,7 +8,8 @@ export const LabelBase = styled.label`
8
8
  font-size: 1rem;
9
9
  line-height: 22px;
10
10
 
11
- color: ${({ theme }) => theme.colors.greyDarker};
11
+ color: ${({ theme, isDisabled }) =>
12
+ isDisabled ? theme.colors.grey : theme.colors.greyDarker};
12
13
 
13
14
  margin: 0 0 6px 4px;
14
15
  `;
@@ -4,6 +4,7 @@ import { CommonProps } from '@global-types/emotion';
4
4
  export interface LabelProps extends CommonProps {
5
5
  htmlFor?: string;
6
6
  children: React.ReactNode;
7
+ isDisabled?: boolean;
7
8
  onMouseEnter?: MouseEventHandler<HTMLLabelElement>;
8
9
  onMouseLeave?: MouseEventHandler<HTMLLabelElement>;
9
10
  }
@@ -23,7 +23,7 @@ const DropdownOptionsBase = styled.ul<{ tabindex?: string }>`
23
23
  overflow-x: hidden;
24
24
  overflow-y: auto;
25
25
 
26
- z-index: 2;
26
+ z-index: 1;
27
27
 
28
28
  filter: ${({ theme }) =>
29
29
  `drop-shadow(-4px 4px 14px ${theme.colors.greyDarker14})`};
@@ -21,10 +21,12 @@ export const Default: StoryObj<typeof ResponsiveImage> = () => {
21
21
  tag
22
22
  </Typography>
23
23
  <ResponsiveImage
24
- srcSet="https://placehold.co/64x64 64w, https://placehold.co/48x48 48w"
25
- sizes="(min-width: 1440px) 64px, 48px"
26
- src="https://placehold.co/48x48"
27
- alt="Steps"
24
+ srcSet={`
25
+ https://firebasestorage.googleapis.com/v0/b/admin-themes.appspot.com/o/logo%2FUI_KIT_Medium_90_51.png?alt=media&token=6761e4f3-b985-4322-8824-0c9668a2e2d9 90w,
26
+ https://firebasestorage.googleapis.com/v0/b/admin-themes.appspot.com/o/logo%2FUI_KIT_Small_80_49.png?alt=media&token=910e7a01-8127-4e2e-88e6-1b85805d7beb 80w`}
27
+ sizes="(max-width: 900px) 80px, (min-width: 900px) 90px"
28
+ src="https://firebasestorage.googleapis.com/v0/b/admin-themes.appspot.com/o/logo%2FUI_KIT_Small_80_49.png?alt=media&token=910e7a01-8127-4e2e-88e6-1b85805d7beb"
29
+ alt="SSA UI Kit"
28
30
  />
29
31
  </div>
30
32
  );
@@ -6,6 +6,7 @@ export const tableFilterPopoverContentStyles = (theme: Theme) => css`
6
6
  padding: 20px 20px 14px 18px;
7
7
  width: 340px;
8
8
  background: ${theme.colors.white};
9
+ z-index: 1;
9
10
  `;
10
11
 
11
12
  export const tableFilterDividerStyles = (theme: Theme) => css`
@@ -148,7 +148,14 @@ export const WithAction: StoryObj<typeof TextField> = () => {
148
148
  register={register}
149
149
  helperText="some nice text"
150
150
  endElement={
151
- <button onClick={() => console.log('calling action...')}>Action</button>
151
+ <button
152
+ onClick={(event) => {
153
+ event.stopPropagation();
154
+ event.preventDefault();
155
+ console.log('calling action...');
156
+ }}>
157
+ Action
158
+ </button>
152
159
  }
153
160
  />
154
161
  );
@@ -0,0 +1,59 @@
1
+ import * as React from 'react';
2
+ import { UseTypeaheadResult } from './useTypeahead';
3
+
4
+ export const TypeaheadContext = React.createContext<UseTypeaheadResult>({
5
+ optionsWithKey: {},
6
+ isMultiple: false,
7
+ selectedItems: [],
8
+ typeaheadId: '',
9
+ firstSuggestion: '',
10
+ inputRef: { current: null },
11
+ triggerRef: { current: null },
12
+ isOpen: false,
13
+ className: undefined,
14
+ startIcon: null,
15
+ endIcon: null,
16
+ startIconClassName: undefined,
17
+ endIconClassName: undefined,
18
+ name: '',
19
+ inputName: '',
20
+ inputValue: '',
21
+ validationSchema: {},
22
+ status: 'basic',
23
+ isDisabled: false,
24
+ options: [],
25
+ placeholder: '',
26
+ setValue: () => {
27
+ /* no-op */
28
+ },
29
+ register: () => {
30
+ return {} as any;
31
+ },
32
+ handleChange: () => {
33
+ /* no-op */
34
+ },
35
+ handleClearAll: () => {
36
+ /* no-op */
37
+ },
38
+ handleInputClick: () => {
39
+ /* no-op */
40
+ },
41
+ handleInputKeyDown: () => {
42
+ /* no-op */
43
+ },
44
+ handleInputChange: () => {
45
+ /* no-op */
46
+ },
47
+ handleOpenChange: () => {
48
+ /* no-op */
49
+ },
50
+ handleRemoveSelectedClick: () => () => {
51
+ /* no-op */
52
+ },
53
+ handleSelectedClick: () => {
54
+ /* no-op */
55
+ },
56
+ });
57
+
58
+ export const useTypeaheadContext = (): UseTypeaheadResult =>
59
+ React.useContext(TypeaheadContext);