@indico-data/design-system 1.0.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 (161) hide show
  1. package/.babelrc +27 -0
  2. package/.eslintignore +6 -0
  3. package/.eslintrc.js +63 -0
  4. package/.husky/pre-commit +4 -0
  5. package/.prettierignore +3 -0
  6. package/.prettierrc +6 -0
  7. package/.stackblitzrc +4 -0
  8. package/.storybook/indico-data-logo.svg +1 -0
  9. package/.storybook/main.ts +36 -0
  10. package/.storybook/preview-head.html +19 -0
  11. package/.storybook/preview.ts +24 -0
  12. package/.storybook/themes.js +24 -0
  13. package/.yarn/releases/yarn-classic.cjs +179386 -0
  14. package/.yarnrc.yml +1 -0
  15. package/README.md +30 -0
  16. package/package.json +79 -0
  17. package/src/components/Accordion/Accordion.stories.tsx +47 -0
  18. package/src/components/Accordion/Accordion.styles.ts +35 -0
  19. package/src/components/Accordion/Accordion.tsx +30 -0
  20. package/src/components/Accordion/index.ts +1 -0
  21. package/src/components/Icon/Icon.stories.tsx +60 -0
  22. package/src/components/Icon/Icon.tsx +75 -0
  23. package/src/components/Icon/faIcons.tsx +168 -0
  24. package/src/components/Icon/index.ts +2 -0
  25. package/src/components/Icon/indicons.tsx +699 -0
  26. package/src/components/Icon/storyHelpers.tsx +87 -0
  27. package/src/components/ListTable/Header/Header.styles.ts +62 -0
  28. package/src/components/ListTable/Header/Header.tsx +67 -0
  29. package/src/components/ListTable/Header/index.ts +1 -0
  30. package/src/components/ListTable/ListTable.stories.tsx +301 -0
  31. package/src/components/ListTable/ListTable.styles.ts +76 -0
  32. package/src/components/ListTable/ListTable.tsx +135 -0
  33. package/src/components/ListTable/index.ts +1 -0
  34. package/src/components/ListTable/mock-data/index.ts +1 -0
  35. package/src/components/ListTable/mock-data/mock-data.ts +291 -0
  36. package/src/components/Pagination/Pagination.stories.tsx +45 -0
  37. package/src/components/Pagination/Pagination.styles.ts +51 -0
  38. package/src/components/Pagination/Pagination.tsx +118 -0
  39. package/src/components/Pagination/index.ts +1 -0
  40. package/src/components/basic-section/Section/Section.stories.tsx +14 -0
  41. package/src/components/basic-section/Section/Section.styles.ts +8 -0
  42. package/src/components/basic-section/Section/Section.tsx +30 -0
  43. package/src/components/basic-section/Section/index.ts +1 -0
  44. package/src/components/basic-section/SectionBlock/SectionBlock.styles.ts +15 -0
  45. package/src/components/basic-section/SectionBlock/SectionBlock.tsx +37 -0
  46. package/src/components/basic-section/SectionBlock/index.ts +1 -0
  47. package/src/components/basic-section/SectionBody/SectionBody.stories.tsx +16 -0
  48. package/src/components/basic-section/SectionBody/SectionBody.styles.ts +18 -0
  49. package/src/components/basic-section/SectionBody/SectionBody.tsx +30 -0
  50. package/src/components/basic-section/SectionBody/index.ts +1 -0
  51. package/src/components/basic-section/SectionHeader/SectionHeader.stories.tsx +17 -0
  52. package/src/components/basic-section/SectionHeader/SectionHeader.styles.ts +5 -0
  53. package/src/components/basic-section/SectionHeader/SectionHeader.tsx +35 -0
  54. package/src/components/basic-section/SectionHeader/index.ts +1 -0
  55. package/src/components/basic-section/SectionTable/SectionTable.styles.ts +237 -0
  56. package/src/components/basic-section/SectionTable/SectionTable.tsx +229 -0
  57. package/src/components/basic-section/SectionTable/index.ts +1 -0
  58. package/src/components/basic-section/index.ts +5 -0
  59. package/src/components/buttons/Button/Button.stories.tsx +80 -0
  60. package/src/components/buttons/Button/Button.styles.ts +99 -0
  61. package/src/components/buttons/Button/Button.tsx +74 -0
  62. package/src/components/buttons/Button/index.ts +1 -0
  63. package/src/components/buttons/IconButton/IconButton.stories.tsx +96 -0
  64. package/src/components/buttons/IconButton/IconButton.styles.ts +78 -0
  65. package/src/components/buttons/IconButton/IconButton.tsx +109 -0
  66. package/src/components/buttons/IconButton/index.ts +1 -0
  67. package/src/components/buttons/commonStyles.ts +108 -0
  68. package/src/components/buttons/index.ts +2 -0
  69. package/src/components/buttons/types.ts +2 -0
  70. package/src/components/dropdowns/BorderSelect/BorderSelect.stories.tsx +22 -0
  71. package/src/components/dropdowns/BorderSelect/BorderSelect.styles.ts +73 -0
  72. package/src/components/dropdowns/BorderSelect/BorderSelect.tsx +85 -0
  73. package/src/components/dropdowns/BorderSelect/index.ts +1 -0
  74. package/src/components/dropdowns/MultiCombobox/MultiCombobox.stories.tsx +146 -0
  75. package/src/components/dropdowns/MultiCombobox/MultiCombobox.styles.ts +89 -0
  76. package/src/components/dropdowns/MultiCombobox/MultiCombobox.tsx +123 -0
  77. package/src/components/dropdowns/MultiCombobox/index.ts +1 -0
  78. package/src/components/dropdowns/Select/Select.stories.tsx +54 -0
  79. package/src/components/dropdowns/Select/Select.styles.ts +73 -0
  80. package/src/components/dropdowns/Select/Select.tsx +69 -0
  81. package/src/components/dropdowns/Select/index.ts +1 -0
  82. package/src/components/dropdowns/SingleCombobox/SingleCombobox.stories.tsx +61 -0
  83. package/src/components/dropdowns/SingleCombobox/SingleCombobox.styles.ts +56 -0
  84. package/src/components/dropdowns/SingleCombobox/SingleCombobox.tsx +103 -0
  85. package/src/components/dropdowns/SingleCombobox/index.ts +1 -0
  86. package/src/components/dropdowns/commonStyles.ts +65 -0
  87. package/src/components/dropdowns/index.ts +4 -0
  88. package/src/components/dropdowns/types.ts +45 -0
  89. package/src/components/dropdowns/useCombobox.ts +32 -0
  90. package/src/components/dropdowns/utils.tsx +25 -0
  91. package/src/components/index.ts +9 -0
  92. package/src/components/inputs/EditableInput/EditableInput.stories.tsx +26 -0
  93. package/src/components/inputs/EditableInput/EditableInput.styles.ts +21 -0
  94. package/src/components/inputs/EditableInput/EditableInput.tsx +103 -0
  95. package/src/components/inputs/EditableInput/index.ts +1 -0
  96. package/src/components/inputs/NumberInput/NumberInput.stories.tsx +72 -0
  97. package/src/components/inputs/NumberInput/NumberInput.styles.ts +66 -0
  98. package/src/components/inputs/NumberInput/NumberInput.tsx +153 -0
  99. package/src/components/inputs/NumberInput/index.ts +1 -0
  100. package/src/components/inputs/SearchInput/SearchInput.stories.tsx +17 -0
  101. package/src/components/inputs/SearchInput/SearchInput.styles.ts +25 -0
  102. package/src/components/inputs/SearchInput/SearchInput.tsx +47 -0
  103. package/src/components/inputs/SearchInput/index.ts +1 -0
  104. package/src/components/inputs/TextInput/TextInput.stories.tsx +104 -0
  105. package/src/components/inputs/TextInput/TextInput.styles.ts +74 -0
  106. package/src/components/inputs/TextInput/TextInput.tsx +116 -0
  107. package/src/components/inputs/TextInput/index.ts +1 -0
  108. package/src/components/inputs/index.ts +4 -0
  109. package/src/components/inputs/inputsCommon.styles.ts +61 -0
  110. package/src/components/loading-indicators/BarSpinner/BarSpinner.stories.tsx +14 -0
  111. package/src/components/loading-indicators/BarSpinner/BarSpinner.styles.ts +53 -0
  112. package/src/components/loading-indicators/BarSpinner/BarSpinner.tsx +21 -0
  113. package/src/components/loading-indicators/BarSpinner/index.ts +1 -0
  114. package/src/components/loading-indicators/CirclePulse/CirclePulse.stories.tsx +22 -0
  115. package/src/components/loading-indicators/CirclePulse/CirclePulse.styles.ts +81 -0
  116. package/src/components/loading-indicators/CirclePulse/CirclePulse.tsx +61 -0
  117. package/src/components/loading-indicators/CirclePulse/index.ts +1 -0
  118. package/src/components/loading-indicators/CircleSpinner/CircleSpinner.stories.tsx +16 -0
  119. package/src/components/loading-indicators/CircleSpinner/CircleSpinner.tsx +37 -0
  120. package/src/components/loading-indicators/CircleSpinner/index.ts +1 -0
  121. package/src/components/loading-indicators/LoadingList/LoadingList.stories.tsx +14 -0
  122. package/src/components/loading-indicators/LoadingList/LoadingList.styles.ts +42 -0
  123. package/src/components/loading-indicators/LoadingList/LoadingList.tsx +9 -0
  124. package/src/components/loading-indicators/LoadingList/index.ts +1 -0
  125. package/src/components/loading-indicators/PercentageRing/PercentageRing.stories.tsx +18 -0
  126. package/src/components/loading-indicators/PercentageRing/PercentageRing.styles.ts +27 -0
  127. package/src/components/loading-indicators/PercentageRing/PercentageRing.tsx +76 -0
  128. package/src/components/loading-indicators/PercentageRing/index.ts +1 -0
  129. package/src/components/loading-indicators/RandomLoadingMessage/RandomLoadingMessage.stories.tsx +16 -0
  130. package/src/components/loading-indicators/RandomLoadingMessage/RandomLoadingMessage.tsx +18 -0
  131. package/src/components/loading-indicators/RandomLoadingMessage/index.ts +1 -0
  132. package/src/components/loading-indicators/RandomLoadingMessage/random-messages.js +67 -0
  133. package/src/components/loading-indicators/index.ts +6 -0
  134. package/src/components/user-feedback/Shrug/Shrug.stories.tsx +38 -0
  135. package/src/components/user-feedback/Shrug/Shrug.styles.ts +23 -0
  136. package/src/components/user-feedback/Shrug/Shrug.tsx +44 -0
  137. package/src/components/user-feedback/Shrug/index.ts +1 -0
  138. package/src/components/user-feedback/index.ts +1 -0
  139. package/src/index.tsx +18 -0
  140. package/src/styles/globals/buttons.ts +154 -0
  141. package/src/styles/globals/forms.ts +103 -0
  142. package/src/styles/globals/index.tsx +25 -0
  143. package/src/styles/globals/layout.ts +25 -0
  144. package/src/styles/globals/lists.ts +23 -0
  145. package/src/styles/globals/margin-padding.ts +33 -0
  146. package/src/styles/globals/media.ts +13 -0
  147. package/src/styles/globals/tables.ts +34 -0
  148. package/src/styles/globals/typography.ts +95 -0
  149. package/src/styles/globals/utility-classes.ts +76 -0
  150. package/src/tokens/animation.ts +6 -0
  151. package/src/tokens/breakpoints.ts +11 -0
  152. package/src/tokens/colors.ts +279 -0
  153. package/src/tokens/index.ts +20 -0
  154. package/src/tokens/margin.ts +5 -0
  155. package/src/tokens/numbers.js +41 -0
  156. package/src/tokens/padding.ts +5 -0
  157. package/src/tokens/spacings.ts +5 -0
  158. package/src/tokens/typography.ts +37 -0
  159. package/src/types.ts +6 -0
  160. package/tsconfig.json +13 -0
  161. package/webpack.config.js +35 -0
@@ -0,0 +1,80 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+
3
+ import { Button } from './Button';
4
+
5
+ const meta = {
6
+ component: Button,
7
+ title: 'buttons/Button',
8
+ argTypes: {
9
+ onClick: { action: 'clicked/pressed' },
10
+ className: { control: { disable: true } },
11
+ },
12
+ } satisfies Meta<typeof Button>;
13
+
14
+ export default meta;
15
+ type Story = StoryObj<typeof Button>;
16
+
17
+ export const Normal: Story = {
18
+ args: {
19
+ id: '1234',
20
+ variant: 'normal',
21
+ type: 'button',
22
+ children: 'Normal button',
23
+ },
24
+ };
25
+
26
+ export const Primary: Story = {
27
+ args: {
28
+ id: '1234',
29
+ variant: 'primary',
30
+ type: 'button',
31
+ children: 'Primary button',
32
+ },
33
+ };
34
+
35
+ export const CallToAction: Story = {
36
+ args: {
37
+ id: '1234',
38
+ variant: 'cta',
39
+ type: 'button',
40
+ children: 'Call to action button',
41
+ },
42
+ };
43
+
44
+ export const LinkStyled: Story = {
45
+ args: {
46
+ id: '1234',
47
+ variant: 'link-style',
48
+ type: 'button',
49
+ children: 'Button styled like a text link',
50
+ },
51
+ };
52
+
53
+ export const UnStyled: Story = {
54
+ args: {
55
+ id: '1234',
56
+ variant: 'no-style',
57
+ type: 'button',
58
+ children: 'Button with no style',
59
+ },
60
+ };
61
+
62
+ export const Disabled: Story = {
63
+ args: {
64
+ id: '1234',
65
+ variant: 'normal',
66
+ type: 'button',
67
+ children: 'Disabled button',
68
+ disabled: true,
69
+ },
70
+ };
71
+
72
+ export const Busy: Story = {
73
+ args: {
74
+ id: '1234',
75
+ variant: 'cta',
76
+ type: 'button',
77
+ children: 'Saving',
78
+ busy: true,
79
+ },
80
+ };
@@ -0,0 +1,99 @@
1
+ import styled from 'styled-components';
2
+
3
+ import { ANIMATION, COLORS, TYPOGRAPHY } from '@/tokens';
4
+
5
+ export const StyledButton = styled.button`
6
+ border: 0;
7
+ display: inline-block;
8
+ cursor: pointer;
9
+ font-family: ${TYPOGRAPHY.fontFamily.base};
10
+
11
+ &.default {
12
+ border-radius: 2px;
13
+ padding: 0.625em 1.25em;
14
+ box-shadow: 0 1px 1px 0 ${COLORS.black};
15
+ transition: background-color ${ANIMATION.duration} ${ANIMATION.timing};
16
+ }
17
+
18
+ &.primary,
19
+ &.cta {
20
+ background-color: ${COLORS.curiousBlue};
21
+ color: ${COLORS.white};
22
+
23
+ &:hover,
24
+ &:focus {
25
+ // todo: shouldn't have to override...
26
+ color: ${COLORS.white};
27
+ // todo: store as constant
28
+ background-color: #1777c2;
29
+ }
30
+ }
31
+
32
+ &.cta {
33
+ font-size: ${TYPOGRAPHY.fontSize.subheadLarge};
34
+ }
35
+
36
+ &.normal {
37
+ font-size: ${TYPOGRAPHY.fontSize.base};
38
+ background-color: ${COLORS.iron};
39
+ color: ${COLORS.darkFontColor};
40
+
41
+ &:hover,
42
+ &:focus {
43
+ // todo: store as constant
44
+ background-color: #bebebe;
45
+ }
46
+ }
47
+
48
+ &.outline {
49
+ color: ${COLORS.midFontColor};
50
+ background: transparent;
51
+ border: 1px solid ${COLORS.midFontColor};
52
+ transition:
53
+ border ${ANIMATION.duration} ${ANIMATION.timing},
54
+ color ${ANIMATION.duration} ${ANIMATION.timing};
55
+
56
+ &:hover,
57
+ &:focus {
58
+ border: 1px solid ${COLORS.defaultFontColor};
59
+ color: ${COLORS.defaultFontColor};
60
+ background: transparent;
61
+ }
62
+
63
+ &:disabled {
64
+ border: 1px solid ${COLORS.midFontColor};
65
+ color: ${COLORS.midFontColor};
66
+ background: transparent;
67
+ opacity: 0.6;
68
+
69
+ &:hover {
70
+ background: transparent;
71
+ border: 1px solid ${COLORS.midFontColor};
72
+ color: ${COLORS.midFontColor};
73
+ opacity: 0.6;
74
+ }
75
+ }
76
+ }
77
+
78
+ &.no-style {
79
+ font-size: inherit;
80
+ color: inherit;
81
+ background-color: transparent;
82
+ box-shadow: none;
83
+ padding: 0;
84
+
85
+ &:hover,
86
+ &:focus {
87
+ color: inherit;
88
+ background-color: transparent;
89
+ }
90
+
91
+ &:not(.focus-visible) {
92
+ outline: none;
93
+ }
94
+ }
95
+
96
+ &.busy {
97
+ cursor: wait;
98
+ }
99
+ `;
@@ -0,0 +1,74 @@
1
+ // TODO: This component's migration was fast-tracked for Insights. Assess for potential refactor and documentation.
2
+
3
+ import React from 'react';
4
+
5
+ import classNames from 'classnames';
6
+
7
+ import { CircleSpinner } from '@/components';
8
+ import { PermafrostComponent } from '@/types';
9
+ import { StyledButton } from './Button.styles';
10
+
11
+ export type ButtonVariant = 'primary' | 'cta' | 'normal' | 'link-style' | 'no-style' | 'outline';
12
+
13
+ export type ButtonProps = PermafrostComponent & {
14
+ 'aria-label'?: string;
15
+ 'aria-pressed'?: boolean;
16
+ 'aria-selected'?: boolean;
17
+ busy?: boolean;
18
+ children: React.ReactNode | React.ReactNode[];
19
+ disabled?: boolean;
20
+ onClick: (...args: any[]) => void;
21
+ onFocus?: () => void;
22
+ role?: string;
23
+ style?: React.CSSProperties;
24
+ tabindex?: 0 | -1;
25
+ type?: 'submit' | 'button' | 'reset';
26
+ variant?: ButtonVariant;
27
+ onMouseEnter?: () => void;
28
+ onMouseLeave?: () => void;
29
+ };
30
+
31
+ export function Button(props: ButtonProps): React.ReactElement {
32
+ const {
33
+ id,
34
+ className,
35
+ busy,
36
+ children,
37
+ disabled,
38
+ onClick,
39
+ onFocus,
40
+ role,
41
+ style,
42
+ tabindex,
43
+ type,
44
+ variant = 'default',
45
+ ...rest
46
+ } = props;
47
+
48
+ return (
49
+ <StyledButton
50
+ aria-disabled={busy}
51
+ aria-label={props['aria-label']}
52
+ aria-pressed={props['aria-pressed']}
53
+ aria-selected={props['aria-selected']}
54
+ className={classNames(variant, className, {
55
+ blue: variant === 'primary' || variant === 'cta',
56
+ busy,
57
+ })}
58
+ data-cy={props['data-cy']}
59
+ disabled={disabled}
60
+ id={id}
61
+ onClick={busy ? undefined : onClick}
62
+ onFocus={onFocus}
63
+ role={role}
64
+ style={style}
65
+ tabIndex={tabindex}
66
+ type={type || 'button'}
67
+ {...rest}
68
+ >
69
+ {busy && <CircleSpinner size="0.9em" style={{ marginRight: '0.5em' }} />}
70
+
71
+ {children}
72
+ </StyledButton>
73
+ );
74
+ }
@@ -0,0 +1 @@
1
+ export { Button } from './Button';
@@ -0,0 +1,96 @@
1
+ import React from 'react';
2
+ import { MemoryRouter } from 'react-router-dom';
3
+ import type { Meta, StoryObj } from '@storybook/react';
4
+
5
+ import { IconButton } from './IconButton';
6
+
7
+ const meta = {
8
+ component: IconButton,
9
+ title: 'buttons/IconButton',
10
+ args: {
11
+ iconSide: 'start',
12
+ size: 'normal',
13
+ variant: 'default',
14
+ },
15
+ argTypes: {
16
+ onPress: { action: 'clicked/pressed' },
17
+ isDisabled: {
18
+ control: {
19
+ type: 'boolean',
20
+ },
21
+ },
22
+ adjustAlignment: {
23
+ description: 'vertical alignment of text relative to icon',
24
+ defaultValue: -2,
25
+ },
26
+ className: {
27
+ control: {
28
+ disable: true,
29
+ },
30
+ },
31
+ style: {
32
+ control: {
33
+ disable: true,
34
+ },
35
+ },
36
+ },
37
+ } satisfies Meta<typeof IconButton>;
38
+
39
+ export default meta;
40
+ type Story = StoryObj<typeof IconButton>;
41
+
42
+ export const Normal: Story = {
43
+ args: {
44
+ iconName: 'fileHappy',
45
+ label: 'Save',
46
+ adjustAlignment: 0,
47
+ },
48
+ };
49
+
50
+ export const NoLabel: Story = {
51
+ args: {
52
+ iconName: 'fileHappy',
53
+ adjustAlignment: 0,
54
+ },
55
+ };
56
+
57
+ export const IconAtEnd: Story = {
58
+ args: {
59
+ iconName: 'fa-arrow-right',
60
+ iconSide: 'end',
61
+ label: 'Next Step',
62
+ variant: 'primary',
63
+ size: 'large',
64
+ },
65
+ };
66
+
67
+ export const Busy: Story = {
68
+ args: {
69
+ iconName: 'fileHappy',
70
+ label: 'Saving',
71
+ busy: true,
72
+ },
73
+ };
74
+
75
+ export const Disabled: Story = {
76
+ args: {
77
+ iconName: 'fileHappy',
78
+ label: 'Save',
79
+ adjustAlignment: 0,
80
+ isDisabled: true,
81
+ },
82
+ };
83
+
84
+ export const AsLink: Story = {
85
+ args: {
86
+ iconName: 'fa-cog',
87
+ isLink: '#',
88
+ label: 'Settings',
89
+ adjustAlignment: 0,
90
+ },
91
+ render: (args) => (
92
+ <MemoryRouter>
93
+ <IconButton {...args} />
94
+ </MemoryRouter>
95
+ ),
96
+ };
@@ -0,0 +1,78 @@
1
+ import styled, { css } from 'styled-components';
2
+ import { Link } from 'react-router-dom';
3
+
4
+ import {
5
+ defaults,
6
+ sizeLarge,
7
+ variantDefault,
8
+ variantDestructive,
9
+ variantPrimary,
10
+ disabledOverrides,
11
+ } from '../commonStyles';
12
+
13
+ const iconMargin = '0.5em';
14
+
15
+ export const iconButton = css`
16
+ align-items: center;
17
+
18
+ ${defaults};
19
+
20
+ svg {
21
+ height: 16px;
22
+ width: 16px;
23
+ }
24
+
25
+ &.large {
26
+ ${sizeLarge};
27
+
28
+ svg {
29
+ height: 20px;
30
+ width: 20px;
31
+ }
32
+ }
33
+
34
+ &.default {
35
+ ${variantDefault};
36
+ }
37
+
38
+ &.destructive {
39
+ ${variantDestructive};
40
+ }
41
+
42
+ &.primary {
43
+ ${variantPrimary};
44
+ }
45
+
46
+ // todo: use this rule/selector after global styling removed
47
+ //:not(:disabled):hover {
48
+ // background-color: #bebebe;
49
+ //}
50
+ ${disabledOverrides};
51
+
52
+ &.start {
53
+ svg.right-margin {
54
+ margin-right: ${iconMargin};
55
+ }
56
+ }
57
+
58
+ &.end {
59
+ svg {
60
+ order: 1;
61
+ margin-left: ${iconMargin};
62
+ }
63
+ }
64
+ `;
65
+
66
+ export const StyledIconButton = styled.button`
67
+ // todo: overrides the global button styling
68
+ display: flex !important;
69
+
70
+ ${iconButton};
71
+ `;
72
+
73
+ export const StyledIconButtonLink = styled(Link)`
74
+ // todo: overrides the global button styling
75
+ display: inline-flex !important;
76
+
77
+ ${iconButton};
78
+ `;
@@ -0,0 +1,109 @@
1
+ // TODO: This component's migration was fast-tracked for Insights. Assess for potential refactor and documentation.
2
+
3
+ import React, { useRef } from 'react';
4
+ import classNames from 'classnames';
5
+ import { useButton } from '@react-aria/button';
6
+ import { useFocusRing } from '@react-aria/focus';
7
+ import { AriaButtonProps } from '@react-types/button';
8
+
9
+ import { Icon, IconName, CircleSpinner } from '@/components';
10
+ import { PermafrostComponent } from '@/types';
11
+ import { ButtonSize, ButtonVariant } from '../types';
12
+
13
+ import { StyledIconButton, StyledIconButtonLink } from './IconButton.styles';
14
+
15
+ type Props = PermafrostComponent & {
16
+ /**
17
+ * Adjusts vertical alignment of the text label, in relation to the icon
18
+ */
19
+ adjustAlignment?: number;
20
+ busy?: boolean;
21
+ iconName: IconName;
22
+ iconSide?: 'start' | 'end';
23
+ isDisabled?: boolean;
24
+ /**
25
+ * If element will be an `<a>`, the href URL
26
+ */
27
+ isLink?: string;
28
+ label?: string;
29
+ size?: ButtonSize;
30
+ style?: React.CSSProperties;
31
+ variant?: ButtonVariant;
32
+ } & AriaButtonProps;
33
+
34
+ /**
35
+ * General utility “button with an icon” component. May also be used for links that _look_
36
+ * like buttons, via the `isLink` property.
37
+ *
38
+ * The button handler must be passed in via the `onPress` property (replaces `onClick`).
39
+ *
40
+ * @see {@link https://react-spectrum.adobe.com/blog/building-a-button-part-1.html}
41
+ */
42
+ export function IconButton(props: Props) {
43
+ const {
44
+ adjustAlignment = 0,
45
+ busy,
46
+ className,
47
+ iconName,
48
+ iconSide = 'start',
49
+ id,
50
+ isDisabled,
51
+ isLink,
52
+ label,
53
+ size = 'normal',
54
+ style,
55
+ variant,
56
+ } = props;
57
+
58
+ const buttonEl = useRef();
59
+ const { buttonProps } = useButton(
60
+ { ...props, onPress: busy ? undefined : props.onPress },
61
+ buttonEl,
62
+ );
63
+ const { isFocusVisible, focusProps } = useFocusRing();
64
+
65
+ const commonProps = {
66
+ 'aria-disabled': busy,
67
+ className: classNames(iconSide, size, variant, className, {
68
+ 'focus-visible': isFocusVisible,
69
+ busy,
70
+ disabled: isLink && isDisabled,
71
+ }),
72
+ ...buttonProps,
73
+ ...focusProps,
74
+ 'data-cy': props['data-cy'],
75
+ id,
76
+ ref: buttonEl,
77
+ style,
78
+ };
79
+
80
+ const iconElement = busy ? (
81
+ <CircleSpinner
82
+ size="1em"
83
+ style={{
84
+ marginTop: `${adjustAlignment}px`,
85
+ marginBottom: `${Math.abs(adjustAlignment)}px`,
86
+ }}
87
+ />
88
+ ) : (
89
+ <Icon className={classNames({ 'right-margin': label })} name={iconName} />
90
+ );
91
+
92
+ const labelElement = label ? (
93
+ <span style={{ marginTop: `${adjustAlignment}px` }}>{label}</span>
94
+ ) : null;
95
+
96
+ if (isLink && !isDisabled) {
97
+ return (
98
+ <StyledIconButtonLink {...commonProps} to={isLink}>
99
+ {iconElement} {labelElement}
100
+ </StyledIconButtonLink>
101
+ );
102
+ }
103
+
104
+ return (
105
+ <StyledIconButton {...commonProps}>
106
+ {iconElement} {labelElement}
107
+ </StyledIconButton>
108
+ );
109
+ }
@@ -0,0 +1 @@
1
+ export { IconButton } from './IconButton';
@@ -0,0 +1,108 @@
1
+ // TODO: This file's migration was fast-tracked for Insights. Assess for potential refactor and documentation.
2
+
3
+ import { css } from 'styled-components';
4
+
5
+ import { ANIMATION, COLORS, TYPOGRAPHY } from '@/tokens';
6
+
7
+ export const defaultHeight = '36px';
8
+ export const largeHeight = '50px';
9
+
10
+ export const basicDefaults = css`
11
+ border: 0;
12
+ display: inline-block;
13
+ cursor: pointer;
14
+
15
+ border-radius: 2px;
16
+ box-shadow: 0 1px 1px 0 ${COLORS.black};
17
+
18
+ &.busy {
19
+ cursor: wait;
20
+ }
21
+
22
+ &:disabled,
23
+ &.disabled {
24
+ opacity: 0.6;
25
+ cursor: not-allowed;
26
+ }
27
+
28
+ &:not(.focus-visible) {
29
+ outline: none;
30
+ }
31
+
32
+ transition-property: opacity, background-color;
33
+ transition-duration: ${ANIMATION.duration};
34
+ transition-timing-function: ${ANIMATION.timing};
35
+ `;
36
+
37
+ export const defaults = css`
38
+ ${basicDefaults};
39
+
40
+ height: ${defaultHeight};
41
+ font-size: ${TYPOGRAPHY.fontSize.base};
42
+ font-family: ${TYPOGRAPHY.fontFamily.base};
43
+
44
+ padding: 0 1em !important;
45
+
46
+ & + button {
47
+ margin-left: 14px;
48
+ }
49
+ `;
50
+
51
+ export const sizeLarge = css`
52
+ height: ${largeHeight};
53
+ font-size: ${TYPOGRAPHY.fontSize.subheadLarge};
54
+ `;
55
+
56
+ export const variantDefault = css`
57
+ color: ${COLORS.darkFontColor};
58
+ background-color: ${COLORS.iron};
59
+
60
+ &:hover {
61
+ background-color: #bebebe;
62
+ }
63
+ `;
64
+
65
+ export const variantPrimary = css`
66
+ background-color: ${COLORS.curiousBlue};
67
+ color: ${COLORS.white};
68
+
69
+ &:hover {
70
+ background-color: #1777c2;
71
+ color: ${COLORS.white};
72
+
73
+ &:disabled {
74
+ background-color: ${COLORS.curiousBlue} !important;
75
+ }
76
+ }
77
+ `;
78
+
79
+ export const variantDestructive = css`
80
+ background-color: ${COLORS.red};
81
+ color: ${COLORS.white};
82
+
83
+ &:hover {
84
+ background-color: #d4464b;
85
+ color: ${COLORS.white};
86
+ }
87
+ `;
88
+
89
+ // todo: remove once globals no longer need to be overridden
90
+ export const disabledOverrides = css`
91
+ &:disabled,
92
+ &.disabled {
93
+ &:hover {
94
+ // todo: override due to default global styling
95
+ background-color: ${COLORS.iron} !important;
96
+
97
+ &.primary {
98
+ // todo: override due to default global styling
99
+ background-color: ${COLORS.curiousBlue} !important;
100
+ }
101
+
102
+ &.destructive {
103
+ // todo: override due to default global styling
104
+ background-color: ${COLORS.red} !important;
105
+ }
106
+ }
107
+ }
108
+ `;
@@ -0,0 +1,2 @@
1
+ export { Button } from './Button';
2
+ export { IconButton } from './IconButton';
@@ -0,0 +1,2 @@
1
+ export type ButtonVariant = 'default' | 'primary' | 'destructive';
2
+ export type ButtonSize = 'normal' | 'large';
@@ -0,0 +1,22 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+
3
+ import { BorderSelect } from './BorderSelect';
4
+
5
+ const meta = {
6
+ component: BorderSelect,
7
+ title: 'dropdowns/BorderSelect',
8
+ argTypes: {},
9
+ args: {
10
+ onChange: () => {},
11
+ options: [
12
+ { name: 'Andy', value: 'andy' },
13
+ { name: 'Jess', value: 'jess' },
14
+ { name: 'Mike', value: 'mike' },
15
+ ],
16
+ },
17
+ } satisfies Meta<typeof BorderSelect>;
18
+
19
+ export default meta;
20
+ type Story = StoryObj<typeof BorderSelect>;
21
+
22
+ export const Base: Story = {};