@thecb/components 4.3.2 → 4.3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thecb/components",
3
- "version": "4.3.2",
3
+ "version": "4.3.3",
4
4
  "description": "Common lib for CityBase react components",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.esm.js",
@@ -63,7 +63,8 @@ const StyledCheckbox = styled.div`
63
63
  checkedStyles,
64
64
  focusedStyles,
65
65
  errorStyles,
66
- disabledStyles
66
+ disabledStyles,
67
+ disabledCheckedStyles
67
68
  }) =>
68
69
  error
69
70
  ? css`
@@ -71,7 +72,7 @@ const StyledCheckbox = styled.div`
71
72
  `
72
73
  : disabled
73
74
  ? css`
74
- ${disabledStyles}
75
+ ${checked ? disabledCheckedStyles : disabledStyles}
75
76
  `
76
77
  : checked
77
78
  ? css`
@@ -90,7 +91,8 @@ const Checkbox = ({
90
91
  disabled = false,
91
92
  themeValues,
92
93
  hidden = false,
93
- error = false
94
+ error = false,
95
+ textExtraStyles
94
96
  }) => {
95
97
  const [focused, setFocused] = useState(false);
96
98
 
@@ -131,6 +133,7 @@ const Checkbox = ({
131
133
  checkedStyles={themeValues.checkedStyles}
132
134
  errorStyles={themeValues.errorStyles}
133
135
  disabledStyles={themeValues.disabledStyles}
136
+ disabledCheckedStyles={themeValues.disabledCheckedStyles}
134
137
  focusedStyles={themeValues.focusedStyles}
135
138
  >
136
139
  <CheckboxIcon
@@ -147,7 +150,13 @@ const Checkbox = ({
147
150
  variant="p"
148
151
  weight={themeValues.textFontWeight}
149
152
  color={themeValues.textColor}
150
- extraStyles={`margin-left: 1rem`}
153
+ extraStyles={
154
+ textExtraStyles
155
+ ? `${textExtraStyles} ${disabled &&
156
+ `color: #6e727e; background-color: #f7f7f7;`} `
157
+ : `margin-left: 1rem ${disabled &&
158
+ `color: #6e727e; background-color: #f7f7f7;`}`
159
+ }
151
160
  >
152
161
  {title}
153
162
  </Text>
@@ -13,12 +13,19 @@ const textFontSize = { default: "1.1rem" };
13
13
  const textFontWeight = { default: "400" };
14
14
  const textLineHeight = { default: "2rem" };
15
15
  const textColor = { default: `${CHARADE_GREY}` };
16
- const disabledCheckColor = { default: `${GHOST_GREY};` };
16
+ const disabledCheckColor = { default: `${WHITE};` };
17
17
  const checkColor = { default: `${WHITE};` };
18
18
  const errorStyles = { default: `border: 1px solid ${RED};` };
19
19
  const focusedStyles = {
20
20
  default: `box-shadow: 0 0 5px 0 ${MATISSE_BLUE};`
21
21
  };
22
+ const disabledCheckedStyles = {
23
+ default: `
24
+ background: #6d717e;
25
+ border: 1px solid #6d717e;
26
+ `
27
+ };
28
+
22
29
  const disabledStyles = {
23
30
  default: `
24
31
  background: ${SEASHELL_WHITE};
@@ -50,5 +57,6 @@ export const fallbackValues = {
50
57
  focusedStyles,
51
58
  disabledStyles,
52
59
  checkedStyles,
53
- defaultStyles
60
+ defaultStyles,
61
+ disabledCheckedStyles
54
62
  };
@@ -17,7 +17,6 @@ import { themeComponent } from "../../../util/themeUtils";
17
17
 
18
18
  const IconWrapper = styled.div`
19
19
  display: flex;
20
- padding-left: 8px;
21
20
  flex-direction: column;
22
21
  justify-content: center;
23
22
  transition: transform 0.3s ease;
@@ -92,7 +91,8 @@ const Dropdown = ({
92
91
  onClick = noop,
93
92
  themeValues,
94
93
  maxHeight,
95
- widthFitOptions = false
94
+ widthFitOptions = false,
95
+ disabled
96
96
  }) => {
97
97
  const [inputValue, setInputValue] = useState("");
98
98
  const [optionsState, setOptionsState] = useState([]);
@@ -171,7 +171,6 @@ const Dropdown = ({
171
171
  useEffect(() => {
172
172
  clearTimeout(timer);
173
173
  setTimer(setTimeout(() => setInputValue(""), 2000));
174
-
175
174
  setFilteredOptions(
176
175
  options.filter(
177
176
  option =>
@@ -205,6 +204,12 @@ const Dropdown = ({
205
204
  width="100%"
206
205
  hoverStyles={`background-color: ${themeValues.hoverColor};`}
207
206
  aria-expanded={isOpen}
207
+ extraStyles={
208
+ disabled &&
209
+ `color: #6e727e;
210
+ background-color: #f7f7f7;
211
+ pointer-events: none;`
212
+ }
208
213
  >
209
214
  <Box
210
215
  as="button"
@@ -222,8 +227,13 @@ const Dropdown = ({
222
227
  }
223
228
  borderRadius="2px"
224
229
  tabIndex={0}
225
- extraStyles={`height: 48px;`}
226
230
  dataQa={placeholder}
231
+ extraStyles={`height: 48px;
232
+ ${disabled &&
233
+ `color: #6e727e;
234
+ background-color: #f7f7f7;
235
+ pointer-events: none;`}
236
+ `}
227
237
  >
228
238
  <Stack direction="row" bottomItem={2}>
229
239
  {isOpen ? (
@@ -234,7 +244,15 @@ const Dropdown = ({
234
244
  themeValues={themeValues}
235
245
  />
236
246
  ) : (
237
- <Text variant="p">
247
+ <Text
248
+ variant="p"
249
+ extraStyles={
250
+ disabled &&
251
+ `color: #6e727e;
252
+ background-color: #f7f7f7;
253
+ pointer-events: none;`
254
+ }
255
+ >
238
256
  {value
239
257
  ? options.find(option => option.value === value)?.text
240
258
  : placeholder}
@@ -40,6 +40,13 @@ const InputField = styled.input`
40
40
  outline-offset: 2px;
41
41
  }
42
42
 
43
+ ${({ disabled }) =>
44
+ disabled &&
45
+ css`
46
+ color: #6e727e;
47
+ background-color: #f7f7f7;
48
+ `}
49
+
43
50
  ${({ extraStyles }) =>
44
51
  css`
45
52
  ${extraStyles}
@@ -76,6 +83,13 @@ const FormattedInputField = styled(({ showErrors, themeValues, ...props }) => (
76
83
  outline-offset: 2px;
77
84
  }
78
85
 
86
+ ${({ disabled }) =>
87
+ disabled &&
88
+ css`
89
+ color: #6e727e;
90
+ background-color: #f7f7f7;
91
+ `}
92
+
79
93
  ${({ extraStyles }) =>
80
94
  css`
81
95
  ${extraStyles}
@@ -17,6 +17,7 @@ const FormSelect = ({
17
17
  onChange,
18
18
  dropdownMaxHeight,
19
19
  disabledValues,
20
+ disabled,
20
21
  themeValues
21
22
  }) => {
22
23
  const [open, setOpen] = useState(false);
@@ -36,7 +37,7 @@ const FormSelect = ({
36
37
  });
37
38
 
38
39
  return (
39
- <SelectContainer ref={dropdownRef}>
40
+ <SelectContainer ref={dropdownRef} disabled={disabled}>
40
41
  <Box padding="0" minWidth="100%">
41
42
  <Cluster justify="space-between" align="center">
42
43
  <Text
@@ -70,6 +71,7 @@ const FormSelect = ({
70
71
  onChange ? value => onChange(value) : value => fieldActions.set(value)
71
72
  }
72
73
  onClick={() => setOpen(!open)}
74
+ disabled={disabled}
73
75
  />
74
76
  <Stack direction="row" justify="space-between">
75
77
  {(field.hasErrors && field.dirty) || (field.hasErrors && showErrors) ? (
@@ -32,6 +32,7 @@ export { default as PasswordRequirements } from "./password-requirements";
32
32
  export { default as Placeholder } from "./placeholder";
33
33
  export { default as ProcessingFee } from "./processing-fee";
34
34
  export { default as RadioButton } from "./radio-button";
35
+ export { default as SearchableSelect } from "./searchable-select";
35
36
  export { default as SolidDivider } from "./solid-divider";
36
37
  export { default as Spinner } from "./spinner";
37
38
  export { default as StateProvinceDropdown } from "./state-province-dropdown";
@@ -0,0 +1,84 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import { fallbackValues } from "./SearchableSelect.theme";
3
+ import { themeComponent } from "../../../util/themeUtils";
4
+ import { Box, Stack } from "../layouts";
5
+ import { FormInput } from "../form-layouts";
6
+ import Checkbox from "../checkbox";
7
+
8
+ const SELECT_ALL = "Select All";
9
+
10
+ const SearchableSelect = ({
11
+ items,
12
+ selectedItems,
13
+ allSelected,
14
+ toggleSelectAllItems,
15
+ selectItem,
16
+ fields,
17
+ actions,
18
+ disabled,
19
+ themeValues
20
+ }) => {
21
+ const [itemList, setItemList] = useState([]);
22
+ useEffect(() => setItemList(items), [items, selectedItems]);
23
+ useEffect(() => {
24
+ const filteredItems = items.filter(item =>
25
+ item?.name
26
+ ?.toLowerCase()
27
+ .includes(fields?.searchTerm?.rawValue?.toLowerCase())
28
+ );
29
+ setItemList(filteredItems);
30
+ }, [fields.searchTerm.rawValue]);
31
+
32
+ const handleSelect = value => {
33
+ if (value.name === SELECT_ALL) {
34
+ allSelected ? toggleSelectAllItems(false) : toggleSelectAllItems(true);
35
+ } else {
36
+ selectItem(value);
37
+ }
38
+ };
39
+ return (
40
+ <Box
41
+ padding="1rem"
42
+ border={themeValues.border}
43
+ extraStyles={
44
+ disabled &&
45
+ `color: #6e727e; background-color: #f7f7f7; pointer-events: none;`
46
+ }
47
+ >
48
+ <Stack>
49
+ <FormInput
50
+ errorMessages={{}}
51
+ field={fields.searchTerm}
52
+ fieldActions={actions.fields.searchTerm}
53
+ placeholder="Search departments"
54
+ disabled={disabled}
55
+ />
56
+ <Box
57
+ padding="0.5rem 0"
58
+ extraStyles={`overflow-y: scroll; height: 250px;`}
59
+ >
60
+ <Stack>
61
+ {itemList.map(value => (
62
+ <Checkbox
63
+ key={value.name}
64
+ title={value.name}
65
+ name={value.name}
66
+ checked={selectedItems.find(item => item.name === value.name)}
67
+ onChange={() => handleSelect(value)}
68
+ textExtraStyles={`margin: 0;`}
69
+ disabled={disabled}
70
+ />
71
+ ))}
72
+ </Stack>
73
+ </Box>
74
+ </Stack>
75
+ </Box>
76
+ );
77
+ };
78
+
79
+ export default themeComponent(
80
+ SearchableSelect,
81
+ "SearchableSelect",
82
+ fallbackValues,
83
+ "default"
84
+ );
@@ -0,0 +1,7 @@
1
+ const border = {
2
+ default: `1px solid #caced8`
3
+ };
4
+
5
+ export const fallbackValues = {
6
+ border
7
+ };
@@ -0,0 +1,3 @@
1
+ import SearchableSelect from "./SearchableSelect";
2
+
3
+ export default SearchableSelect;
@@ -8,6 +8,7 @@ export { default as EmailForm } from "./email-form";
8
8
  export { default as ForgotPasswordForm } from "./forgot-password-form";
9
9
  export { default as HighlightTabRow } from "./highlight-tab-row";
10
10
  export { iconsMap as ObligationIcons } from "./obligation/icons";
11
+ export { default as InternalUserInfoForm } from "./internal-user-info-form";
11
12
  export { default as LoginForm } from "./login-form";
12
13
  export { default as Modal } from "./modal";
13
14
  export { default as Module } from "./module";
@@ -0,0 +1,165 @@
1
+ import React, { useEffect } from "react";
2
+ import { required } from "redux-freeform";
3
+ import { Stack } from "../../atoms/layouts";
4
+ import Heading from "../../atoms/heading";
5
+ import { noop } from "../../../util/general";
6
+ import {
7
+ FormInput,
8
+ FormContainer,
9
+ FormInputColumn
10
+ } from "../../atoms/form-layouts";
11
+ import FormSelect from "../../atoms/form-select";
12
+ import SearchableSelect from "../../atoms/searchable-select";
13
+
14
+ const RESEARCHER = "RESEARCHER";
15
+ const AGENCY_ADMIN = "AGENCY_ADMIN";
16
+ const CLIENT_ADMIN = "CLIENT_ADMIN";
17
+ const SUPERVISOR = "SUPERVISOR";
18
+ const CB_ADMIN = "CITYBASE_ADMIN";
19
+ const CREATE_CLIENT_ADMIN = "CREATE_CLIENT_ADMIN";
20
+
21
+ const roleDescriptions = {
22
+ [RESEARCHER]: "Researcher",
23
+ [AGENCY_ADMIN]: "Agency admin",
24
+ [CLIENT_ADMIN]: "Client admin",
25
+ [SUPERVISOR]: "Supervisor",
26
+ [CB_ADMIN]: "Citybase admin"
27
+ };
28
+
29
+ const InternalUserInfoForm = ({
30
+ variant = "default",
31
+ fields,
32
+ actions,
33
+ clearOnDismount,
34
+ showErrors,
35
+ handleSubmit = noop,
36
+ allAgencyOptions,
37
+ selectedAgencies,
38
+ allSelected,
39
+ toggleSelectAllAgencies,
40
+ selectAgency,
41
+ roleOptions,
42
+ clientOptions,
43
+ disabled,
44
+ emailDisabled,
45
+ roleDisabled,
46
+ formType
47
+ }) => {
48
+ useEffect(() => {
49
+ if (formType === CREATE_CLIENT_ADMIN) {
50
+ actions.fields.client.addValidator(required());
51
+ }
52
+ }, []);
53
+ if (clearOnDismount) {
54
+ useEffect(() => () => actions.form.clear(), []);
55
+ }
56
+ const clientErrorMessages = {
57
+ [required.error]: "Client is required"
58
+ };
59
+ const firstNameErrorMessages = {
60
+ [required.error]: "First name is required"
61
+ };
62
+ const lastNameErrorMessages = {
63
+ [required.error]: "Last name is required"
64
+ };
65
+ const emailErrorMessages = {
66
+ [required.error]: "Email is required"
67
+ };
68
+ const roleErrorMessages = {
69
+ [required.error]: "Role is required"
70
+ };
71
+ return (
72
+ <FormContainer variant={variant} role="form" aria-label="user-info-form">
73
+ <Stack>
74
+ {formType === CREATE_CLIENT_ADMIN && (
75
+ <FormInputColumn>
76
+ <Heading variant="h6" weight="700" margin="0 0 1rem">
77
+ Select Client
78
+ </Heading>
79
+ <FormSelect
80
+ labelTextWhenNoError="Client"
81
+ errorMessages={clientErrorMessages}
82
+ options={clientOptions.map(client => ({
83
+ text: client,
84
+ value: client,
85
+ id: client
86
+ }))}
87
+ field={fields.client}
88
+ fieldActions={actions.fields.client}
89
+ showErrors={showErrors}
90
+ onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
91
+ />
92
+ </FormInputColumn>
93
+ )}
94
+ <FormInputColumn>
95
+ <Heading variant="h6" weight="700" margin="0 0 1rem">
96
+ Personal Information
97
+ </Heading>
98
+ <FormInput
99
+ labelTextWhenNoError="First Name"
100
+ errorMessages={firstNameErrorMessages}
101
+ field={fields.firstName}
102
+ fieldActions={actions.fields.firstName}
103
+ showErrors={showErrors}
104
+ onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
105
+ />
106
+ <FormInput
107
+ labelTextWhenNoError="Last Name"
108
+ errorMessages={lastNameErrorMessages}
109
+ field={fields.lastName}
110
+ fieldActions={actions.fields.lastName}
111
+ showErrors={showErrors}
112
+ onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
113
+ />
114
+ <FormInput
115
+ labelTextWhenNoError="Email"
116
+ errorMessages={emailErrorMessages}
117
+ field={fields.email}
118
+ fieldActions={actions.fields.email}
119
+ showErrors={showErrors}
120
+ onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
121
+ disabled={disabled || emailDisabled}
122
+ />
123
+ </FormInputColumn>
124
+ <FormInputColumn>
125
+ <Heading variant="h6" weight="700" margin="0 0 1rem">
126
+ Dashboard Settings
127
+ </Heading>
128
+ <FormSelect
129
+ labelTextWhenNoError="Role"
130
+ errorMessages={roleErrorMessages}
131
+ options={roleOptions.map(role => ({
132
+ text: roleDescriptions[role],
133
+ value: role,
134
+ id: role
135
+ }))}
136
+ field={fields.role}
137
+ fieldActions={actions.fields.role}
138
+ showErrors={showErrors}
139
+ onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
140
+ disabled={disabled || roleDisabled}
141
+ />
142
+ </FormInputColumn>
143
+ {formType !== CREATE_CLIENT_ADMIN && (
144
+ <FormInputColumn>
145
+ <Heading variant="h6" weight="700" margin="0 0 1rem">
146
+ Select Agencies
147
+ </Heading>
148
+ <SearchableSelect
149
+ actions={actions}
150
+ fields={fields}
151
+ items={allAgencyOptions}
152
+ selectedItems={selectedAgencies}
153
+ allSelected={allSelected}
154
+ toggleSelectAllItems={toggleSelectAllAgencies}
155
+ selectItem={selectAgency}
156
+ disabled={disabled}
157
+ />
158
+ </FormInputColumn>
159
+ )}
160
+ </Stack>
161
+ </FormContainer>
162
+ );
163
+ };
164
+
165
+ export default InternalUserInfoForm;
@@ -0,0 +1,26 @@
1
+ import { createFormState, required, isProbablyEmail } from "redux-freeform";
2
+
3
+ const formConfig = {
4
+ client: {
5
+ validators: []
6
+ },
7
+ firstName: {
8
+ validators: [required()]
9
+ },
10
+ lastName: {
11
+ validators: [required()]
12
+ },
13
+ email: {
14
+ validators: [required(), isProbablyEmail()]
15
+ },
16
+ role: {
17
+ validators: [required()]
18
+ },
19
+ searchTerm: {
20
+ validators: []
21
+ }
22
+ };
23
+
24
+ export const { reducer, mapStateToProps, mapDispatchToProps } = createFormState(
25
+ formConfig
26
+ );
@@ -0,0 +1,11 @@
1
+ import InternalUserInfoForm from "./InternalUserInfoForm";
2
+ import {
3
+ reducer,
4
+ mapStateToProps,
5
+ mapDispatchToProps
6
+ } from "./InternalUserInfoForm.state";
7
+
8
+ InternalUserInfoForm.reducer = reducer;
9
+ InternalUserInfoForm.mapStateToProps = mapStateToProps;
10
+ InternalUserInfoForm.mapDispatchToProps = mapDispatchToProps;
11
+ export default InternalUserInfoForm;