goobs-frontend 0.8.27 → 0.8.28

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "goobs-frontend",
3
- "version": "0.8.27",
3
+ "version": "0.8.28",
4
4
  "type": "module",
5
5
  "description": "A comprehensive React-based UI library built on Material-UI, offering a wide range of customizable components including grids, typography, buttons, cards, forms, navigation, pricing tables, steppers, tooltips, accordions, and more. Designed for building responsive and consistent user interfaces with advanced features like form validation, theming, and code syntax highlighting.",
6
6
  "license": "MIT",
@@ -59,7 +59,7 @@
59
59
  "@typescript-eslint/eslint-plugin": "^8.21.0",
60
60
  "@typescript-eslint/parser": "^8.21.0",
61
61
  "chromatic": "^11.25.1",
62
- "eslint": "^9.18.0",
62
+ "eslint": "^9.19.0",
63
63
  "eslint-config-next": "^15.1.6",
64
64
  "eslint-config-prettier": "^10.0.1",
65
65
  "eslint-plugin-prettier": "^5.2.3",
@@ -92,26 +92,17 @@
92
92
  "node.js",
93
93
  "formData",
94
94
  "form",
95
- "form-validation",
96
- "form-submission",
97
95
  "button",
98
96
  "grid",
99
97
  "responsive",
100
- "flexgrid",
101
98
  "typography",
102
- "responsive-grid",
103
- "styled-component",
104
- "input-component",
105
- "input-validation",
106
- "button-validation",
107
- "form-validation",
99
+ "button",
108
100
  "searchbar",
109
101
  "dropdown",
110
- "multilinetextfield",
102
+ "complexeditor",
111
103
  "textfield",
112
104
  "phonenumber",
113
105
  "password",
114
- "email",
115
106
  "number",
116
107
  "card",
117
108
  "pricing-table",
@@ -120,20 +111,19 @@
120
111
  "tooltip",
121
112
  "accordion",
122
113
  "code-copy",
123
- "syntax-highlighting",
124
- "theming",
125
114
  "customizable-components",
126
115
  "react-components",
127
116
  "ui-library",
128
- "design-system",
129
117
  "front-end",
130
- "user-interface",
131
118
  "responsive-design",
132
119
  "typescript"
133
120
  ],
134
121
  "resolutions": {
135
122
  "string-width": "^4.2.3",
136
- "strip-ansi": "^6.0.1"
123
+ "strip-ansi": "^6.0.1",
124
+ "webpack": "^5.0.0",
125
+ "glob": "^9.0.0",
126
+ "rimraf": "^4.0.0"
137
127
  },
138
128
  "eslintConfig": {
139
129
  "extends": [
@@ -1,6 +1,6 @@
1
1
  'use client'
2
2
  import React from 'react'
3
- import ProjectBoard from '../../../ProjectBoard'
3
+ import ProjectBoard from '../../../ProjectBoard/'
4
4
  import { ProjectBoardProps } from '../../../ProjectBoard/types'
5
5
  import { columnconfig, cellconfig } from '../../../Grid'
6
6
 
@@ -0,0 +1,245 @@
1
+ import * as React from 'react'
2
+ import { Theme, useTheme, styled, SxProps, alpha } from '@mui/material/styles'
3
+ import Box from '@mui/material/Box'
4
+ import OutlinedInput from '@mui/material/OutlinedInput'
5
+ import InputLabel from '@mui/material/InputLabel'
6
+ import MenuItem from '@mui/material/MenuItem'
7
+ import FormControl, { FormControlProps } from '@mui/material/FormControl'
8
+ import Select, { SelectChangeEvent } from '@mui/material/Select'
9
+ import Chip from '@mui/material/Chip'
10
+
11
+ export interface MultiSelectChipProps
12
+ extends Omit<FormControlProps, 'onChange'> {
13
+ label?: React.ReactNode
14
+ options?: string[]
15
+ defaultSelected?: string[]
16
+ onChange?: (values: string[]) => void
17
+
18
+ backgroundcolor?: string
19
+ outlinecolor?: string
20
+ fontcolor?: string
21
+ inputfontcolor?: string
22
+ shrunkfontcolor?: string
23
+ unshrunkfontcolor?: string
24
+ placeholdercolor?: string
25
+
26
+ shrunklabelposition?: 'onNotch' | 'aboveNotch'
27
+ sx?: SxProps
28
+ }
29
+
30
+ const ITEM_HEIGHT = 40
31
+ const ITEM_PADDING_TOP = 8
32
+ const MenuProps = {
33
+ PaperProps: {
34
+ style: {
35
+ maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
36
+ width: 250,
37
+ },
38
+ },
39
+ }
40
+
41
+ function getStyles(
42
+ name: string,
43
+ selectedArray: readonly string[],
44
+ theme: Theme
45
+ ) {
46
+ return {
47
+ fontWeight: selectedArray.includes(name)
48
+ ? theme.typography.fontWeightMedium
49
+ : theme.typography.fontWeightRegular,
50
+ }
51
+ }
52
+
53
+ const StyledFormControl = styled(FormControl, {
54
+ shouldForwardProp: prop =>
55
+ ![
56
+ 'backgroundcolor',
57
+ 'outlinecolor',
58
+ 'fontcolor',
59
+ 'inputfontcolor',
60
+ 'shrunkfontcolor',
61
+ 'unshrunkfontcolor',
62
+ 'placeholdercolor',
63
+ 'shrunklabelposition',
64
+ ].includes(prop as string),
65
+ })<
66
+ Pick<
67
+ MultiSelectChipProps,
68
+ | 'backgroundcolor'
69
+ | 'outlinecolor'
70
+ | 'fontcolor'
71
+ | 'inputfontcolor'
72
+ | 'shrunkfontcolor'
73
+ | 'unshrunkfontcolor'
74
+ | 'placeholdercolor'
75
+ | 'shrunklabelposition'
76
+ > & { hasvalue: string }
77
+ >(
78
+ ({
79
+ backgroundcolor,
80
+ outlinecolor,
81
+ fontcolor,
82
+ inputfontcolor,
83
+ shrunkfontcolor,
84
+ unshrunkfontcolor,
85
+ placeholdercolor,
86
+ shrunklabelposition,
87
+ hasvalue,
88
+ }) => ({
89
+ '& .MuiOutlinedInput-root': {
90
+ minHeight: '40px',
91
+ backgroundColor: backgroundcolor || 'inherit',
92
+ color: fontcolor || 'inherit',
93
+ '& fieldset': {
94
+ borderColor:
95
+ outlinecolor || (hasvalue === 'true' ? 'black' : 'rgba(0,0,0,0.23)'),
96
+ },
97
+ '&:hover fieldset': {
98
+ borderColor:
99
+ outlinecolor || (hasvalue === 'true' ? 'black' : 'rgba(0,0,0,0.23)'),
100
+ },
101
+ '&.Mui-focused fieldset': {
102
+ borderColor:
103
+ outlinecolor || (hasvalue === 'true' ? 'black' : 'rgba(0,0,0,0.23)'),
104
+ },
105
+ '& input': {
106
+ color: inputfontcolor || fontcolor || 'inherit',
107
+ '&::placeholder': {
108
+ color: placeholdercolor || alpha('#000', 0.54),
109
+ },
110
+ },
111
+ '& .MuiSelect-icon': {
112
+ color: inputfontcolor || fontcolor || 'inherit',
113
+ },
114
+ },
115
+ '& .MuiInputLabel-root': {
116
+ color: unshrunkfontcolor || fontcolor || 'inherit',
117
+ '&.Mui-focused': {
118
+ color: shrunkfontcolor || fontcolor || 'inherit',
119
+ },
120
+ '&.MuiInputLabel-shrink': {
121
+ color: shrunkfontcolor || fontcolor || 'inherit',
122
+ ...(shrunklabelposition === 'aboveNotch' && {
123
+ transform: 'translate(0px, -17px) scale(0.75)',
124
+ }),
125
+ ...(shrunklabelposition === 'onNotch' && {
126
+ transform: 'translate(13px, -4px) scale(0.75)',
127
+ }),
128
+ },
129
+ },
130
+ })
131
+ )
132
+
133
+ export default function MultipleSelectChip(props: MultiSelectChipProps) {
134
+ const {
135
+ label = 'Chip',
136
+ options = [],
137
+ defaultSelected = [],
138
+ onChange,
139
+
140
+ backgroundcolor,
141
+ outlinecolor,
142
+ fontcolor,
143
+ inputfontcolor,
144
+ shrunkfontcolor,
145
+ unshrunkfontcolor,
146
+ placeholdercolor,
147
+ shrunklabelposition,
148
+
149
+ sx,
150
+ ...rest
151
+ } = props
152
+
153
+ const theme = useTheme()
154
+ const [selectedValues, setSelectedValues] =
155
+ React.useState<string[]>(defaultSelected)
156
+
157
+ const hasValue = React.useMemo(
158
+ () => (selectedValues.length > 0).toString(),
159
+ [selectedValues]
160
+ )
161
+
162
+ const handleSelectChange = (
163
+ event: SelectChangeEvent<typeof selectedValues>
164
+ ) => {
165
+ const { value } = event.target
166
+ const newValue = typeof value === 'string' ? value.split(',') : value
167
+ setSelectedValues(newValue)
168
+ if (onChange) {
169
+ onChange(newValue)
170
+ }
171
+ }
172
+
173
+ return (
174
+ <Box sx={{ display: 'flex', justifyContent: 'center', mt: 2 }}>
175
+ <StyledFormControl
176
+ sx={{ width: 300, ...sx }}
177
+ variant="outlined"
178
+ hasvalue={hasValue}
179
+ backgroundcolor={backgroundcolor}
180
+ outlinecolor={outlinecolor}
181
+ fontcolor={fontcolor}
182
+ inputfontcolor={inputfontcolor}
183
+ shrunkfontcolor={shrunkfontcolor}
184
+ unshrunkfontcolor={unshrunkfontcolor}
185
+ placeholdercolor={placeholdercolor}
186
+ shrunklabelposition={shrunklabelposition}
187
+ {...rest}
188
+ >
189
+ <InputLabel id="multi-select-chip-label">{label}</InputLabel>
190
+ <Select
191
+ labelId="multi-select-chip-label"
192
+ id="multi-select-chip"
193
+ multiple
194
+ /**
195
+ * Remove any fixed height. Let the
196
+ * OutlinedInput's styles control minHeight.
197
+ */
198
+ value={selectedValues}
199
+ onChange={handleSelectChange}
200
+ input={
201
+ <OutlinedInput
202
+ label={label}
203
+ /**
204
+ * Give the input an initial minHeight (e.g. 55px),
205
+ * and allow it to wrap chips, thus expanding the height
206
+ */
207
+ sx={{
208
+ minHeight: 55,
209
+ display: 'flex',
210
+ flexWrap: 'wrap',
211
+ gap: 0.5,
212
+ alignItems: 'center',
213
+ }}
214
+ placeholder={placeholdercolor ? (label as string) : undefined}
215
+ />
216
+ }
217
+ renderValue={selected => (
218
+ <Box
219
+ sx={{
220
+ display: 'flex',
221
+ flexWrap: 'wrap',
222
+ gap: 0.5,
223
+ }}
224
+ >
225
+ {selected.map(val => (
226
+ <Chip key={val} label={val} />
227
+ ))}
228
+ </Box>
229
+ )}
230
+ MenuProps={MenuProps}
231
+ >
232
+ {options.map(name => (
233
+ <MenuItem
234
+ key={name}
235
+ value={name}
236
+ style={getStyles(name, selectedValues, theme)}
237
+ >
238
+ {name}
239
+ </MenuItem>
240
+ ))}
241
+ </Select>
242
+ </StyledFormControl>
243
+ </Box>
244
+ )
245
+ }
@@ -0,0 +1,129 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ // If using the official SB testing library + jest:
3
+ // import { within, userEvent } from '@storybook/testing-library';
4
+ // import { expect } from '@storybook/jest';
5
+ //
6
+ // If you're using the same approach from your example:
7
+ import { within, userEvent, expect } from '@storybook/test'
8
+
9
+ import MultipleSelectChip from './index'
10
+
11
+ const meta: Meta<typeof MultipleSelectChip> = {
12
+ title: 'Components/MultiSelectChip',
13
+ component: MultipleSelectChip,
14
+ parameters: {
15
+ a11y: { disable: false },
16
+ },
17
+ }
18
+ export default meta
19
+
20
+ type Story = StoryObj<typeof MultipleSelectChip>
21
+
22
+ // Example data array
23
+ const NAMES = [
24
+ 'Oliver Hansen',
25
+ 'Van Henry',
26
+ 'April Tucker',
27
+ 'Ralph Hubbard',
28
+ 'Omar Alexander',
29
+ 'Carlos Abbott',
30
+ 'Miriam Wagner',
31
+ 'Bradley Wilkerson',
32
+ 'Virginia Andrews',
33
+ 'Kelly Snyder',
34
+ ]
35
+
36
+ /**
37
+ * 1) Basic usage - No preselected items.
38
+ */
39
+ export const Basic: Story = {
40
+ name: 'Basic (Default)',
41
+ args: {
42
+ label: 'Select a Name',
43
+ options: NAMES,
44
+ defaultSelected: [],
45
+ },
46
+ play: async ({ canvasElement }) => {
47
+ const canvas = within(canvasElement)
48
+
49
+ // 1. Verify label is visible
50
+ expect(canvas.getByLabelText('Select a Name')).toBeInTheDocument()
51
+
52
+ // 2. Click the Select to open the dropdown
53
+ await userEvent.click(canvas.getByLabelText('Select a Name'))
54
+
55
+ // 3. Choose "Van Henry" from the list
56
+ await userEvent.click(canvas.getByText('Van Henry'))
57
+
58
+ // 4. Now "Van Henry" should appear as a chip
59
+ expect(canvas.getByText('Van Henry')).toBeInTheDocument()
60
+ },
61
+ }
62
+
63
+ /**
64
+ * 2) PreSelected
65
+ */
66
+ export const PreSelected: Story = {
67
+ args: {
68
+ label: 'PreSelected Names',
69
+ options: NAMES,
70
+ defaultSelected: ['April Tucker', 'Omar Alexander'],
71
+ },
72
+ play: ({ canvasElement }) => {
73
+ const canvas = within(canvasElement)
74
+ // Both pre-selected chips should be shown
75
+ expect(canvas.getByText('April Tucker')).toBeInTheDocument()
76
+ expect(canvas.getByText('Omar Alexander')).toBeInTheDocument()
77
+ },
78
+ }
79
+
80
+ /**
81
+ * 3) Multiple Selections
82
+ */
83
+ export const MultipleSelections: Story = {
84
+ args: {
85
+ label: 'Pick Multiple',
86
+ options: NAMES,
87
+ defaultSelected: [],
88
+ },
89
+ play: async ({ canvasElement }) => {
90
+ const canvas = within(canvasElement)
91
+
92
+ // Open the menu
93
+ await userEvent.click(canvas.getByLabelText('Pick Multiple'))
94
+ // Select "Van Henry"
95
+ await userEvent.click(canvas.getByText('Van Henry'))
96
+ expect(canvas.getByText('Van Henry')).toBeInTheDocument()
97
+
98
+ // Menu closes after selection, so open again
99
+ await userEvent.click(canvas.getByLabelText('Pick Multiple'))
100
+ await userEvent.click(canvas.getByText('April Tucker'))
101
+ expect(canvas.getByText('April Tucker')).toBeInTheDocument()
102
+ },
103
+ }
104
+
105
+ /**
106
+ * 4) Custom Styles - Example usage of props like backgroundcolor, outlinecolor, etc.
107
+ */
108
+ export const CustomStyles: Story = {
109
+ args: {
110
+ label: 'Custom Colors',
111
+ options: NAMES,
112
+ defaultSelected: ['Kelly Snyder'],
113
+ backgroundcolor: '#f3e5f5', // Light purple
114
+ outlinecolor: '#6a1b9a', // Dark purple
115
+ fontcolor: '#283593', // Indigo for text
116
+ },
117
+ play: async ({ canvasElement }) => {
118
+ const canvas = within(canvasElement)
119
+
120
+ // Confirm that Kelly Snyder is initially selected
121
+ expect(canvas.getByText('Kelly Snyder')).toBeInTheDocument()
122
+
123
+ // Let's open the menu and deselect
124
+ await userEvent.click(canvas.getByLabelText('Custom Colors'))
125
+ await userEvent.click(canvas.getByText('Kelly Snyder'))
126
+
127
+ expect(canvas.queryByText('Kelly Snyder')).not.toBeInTheDocument()
128
+ },
129
+ }