@strapi/plugin-color-picker 0.0.0
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/README.md +31 -0
- package/admin/src/components/ColorPicker/ColorPickerIcon/index.js +26 -0
- package/admin/src/components/ColorPicker/ColorPickerInput/index.js +201 -0
- package/admin/src/components/tests/__snapshots__/color-picker-input.test.js.snap +252 -0
- package/admin/src/components/tests/color-picker-input.test.js +48 -0
- package/admin/src/index.js +86 -0
- package/admin/src/pluginId.js +5 -0
- package/admin/src/translations/en.json +12 -0
- package/admin/src/translations/fr.json +1 -0
- package/admin/src/utils/getTrad.js +5 -0
- package/color-picker.png +0 -0
- package/jest.config.front.js +10 -0
- package/jest.config.js +10 -0
- package/package.json +39 -0
- package/server/index.js +7 -0
- package/server/register.js +9 -0
- package/strapi-admin.js +3 -0
- package/strapi-server.js +3 -0
package/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Strapi plugin Color Picker
|
|
2
|
+
|
|
3
|
+
A Strapi-maintainted color picker custom field
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
To install this plugin, you need to add an NPM dependency to your Strapi application:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
# Using Yarn
|
|
11
|
+
yarn add @strapi/plugin-color-picker
|
|
12
|
+
|
|
13
|
+
# Or using NPM
|
|
14
|
+
npm install @strapi/plugin-color-picker
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Then, you'll need to build your admin panel:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
# Using Yarn
|
|
21
|
+
yarn build
|
|
22
|
+
|
|
23
|
+
# Or using NPM
|
|
24
|
+
npm run build
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
Use saturation and hue sliders to select a color and save the value as a HEX string
|
|
30
|
+
|
|
31
|
+

|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { Icon } from '@strapi/design-system/Icon';
|
|
4
|
+
import { Flex } from '@strapi/design-system/Flex';
|
|
5
|
+
import Paint from '@strapi/icons/Paint';
|
|
6
|
+
|
|
7
|
+
const IconBox = styled(Flex)`
|
|
8
|
+
/* Hard code color values */
|
|
9
|
+
/* to stay consistent between themes */
|
|
10
|
+
background-color: #f0f0ff; /* primary100 */
|
|
11
|
+
border: 1px solid #d9d8ff; /* primary200 */
|
|
12
|
+
|
|
13
|
+
svg > path {
|
|
14
|
+
fill: #4945ff; /* primary600 */
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const ColorPickerIcon = () => {
|
|
19
|
+
return (
|
|
20
|
+
<IconBox justifyContent="center" alignItems="center" width={7} height={6} hasRadius aria-hidden>
|
|
21
|
+
<Icon as={Paint} />
|
|
22
|
+
</IconBox>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default ColorPickerIcon;
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import React, { useState, useRef } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import { Stack } from '@strapi/design-system/Stack';
|
|
5
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
6
|
+
import { Flex } from '@strapi/design-system/Flex';
|
|
7
|
+
import { Box } from '@strapi/design-system/Box';
|
|
8
|
+
import { BaseButton } from '@strapi/design-system/BaseButton';
|
|
9
|
+
import { FocusTrap } from '@strapi/design-system/FocusTrap';
|
|
10
|
+
import { Field, FieldHint, FieldError, FieldLabel, FieldInput } from '@strapi/design-system/Field';
|
|
11
|
+
import { Popover } from '@strapi/design-system/Popover';
|
|
12
|
+
import CarretDown from '@strapi/icons/CarretDown';
|
|
13
|
+
import { useIntl } from 'react-intl';
|
|
14
|
+
import { HexColorPicker } from 'react-colorful';
|
|
15
|
+
|
|
16
|
+
import getTrad from '../../../utils/getTrad';
|
|
17
|
+
|
|
18
|
+
const ColorPreview = styled.div`
|
|
19
|
+
border-radius: 50%;
|
|
20
|
+
width: 20px;
|
|
21
|
+
height: 20px;
|
|
22
|
+
margin-right: 10px;
|
|
23
|
+
background-color: ${(props) => props.color};
|
|
24
|
+
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const ColorPicker = styled(HexColorPicker)`
|
|
28
|
+
&& {
|
|
29
|
+
width: 100%;
|
|
30
|
+
aspect-ratio: 1.5;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.react-colorful__pointer {
|
|
34
|
+
width: ${({ theme }) => theme.spaces[3]};
|
|
35
|
+
height: ${({ theme }) => theme.spaces[3]};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.react-colorful__saturation {
|
|
39
|
+
border-radius: ${({ theme }) => theme.spaces[1]};
|
|
40
|
+
border-bottom: none;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.react-colorful__hue {
|
|
44
|
+
border-radius: 10px;
|
|
45
|
+
height: ${({ theme }) => theme.spaces[3]};
|
|
46
|
+
margin-top: ${({ theme }) => theme.spaces[2]};
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
const ColorPickerToggle = styled(BaseButton)`
|
|
51
|
+
display: flex;
|
|
52
|
+
justify-content: space-between;
|
|
53
|
+
align-items: center;
|
|
54
|
+
|
|
55
|
+
svg {
|
|
56
|
+
width: ${({ theme }) => theme.spaces[2]};
|
|
57
|
+
height: ${({ theme }) => theme.spaces[2]};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
svg > path {
|
|
61
|
+
fill: ${({ theme }) => theme.colors.neutral500};
|
|
62
|
+
justify-self: flex-end;
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
const ColorPickerPopover = styled(Popover)`
|
|
67
|
+
padding: ${({ theme }) => theme.spaces[2]};
|
|
68
|
+
min-height: 270px;
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
const ColorPickerInput = ({
|
|
72
|
+
attribute,
|
|
73
|
+
description,
|
|
74
|
+
disabled,
|
|
75
|
+
error,
|
|
76
|
+
intlLabel,
|
|
77
|
+
labelAction,
|
|
78
|
+
name,
|
|
79
|
+
onChange,
|
|
80
|
+
required,
|
|
81
|
+
value,
|
|
82
|
+
}) => {
|
|
83
|
+
const [showColorPicker, setShowColorPicker] = useState(false);
|
|
84
|
+
const colorPickerButtonRef = useRef();
|
|
85
|
+
const { formatMessage } = useIntl();
|
|
86
|
+
const color = value || '#000000';
|
|
87
|
+
const styleUppercase = { textTransform: 'uppercase' };
|
|
88
|
+
|
|
89
|
+
const handleBlur = (e) => {
|
|
90
|
+
e.preventDefault();
|
|
91
|
+
|
|
92
|
+
if (!e.currentTarget.contains(e.relatedTarget)) {
|
|
93
|
+
setShowColorPicker(false);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<Field
|
|
99
|
+
name={name}
|
|
100
|
+
id={name}
|
|
101
|
+
// GenericInput calls formatMessage and returns a string for the error
|
|
102
|
+
error={error}
|
|
103
|
+
hint={description && formatMessage(description)}
|
|
104
|
+
>
|
|
105
|
+
<Stack spacing={1}>
|
|
106
|
+
<FieldLabel action={labelAction} required={required}>
|
|
107
|
+
{formatMessage(intlLabel)}
|
|
108
|
+
</FieldLabel>
|
|
109
|
+
<ColorPickerToggle
|
|
110
|
+
ref={colorPickerButtonRef}
|
|
111
|
+
aria-label={formatMessage({
|
|
112
|
+
id: getTrad('color-picker.toggle.aria-label'),
|
|
113
|
+
defaultMessage: 'Color picker toggle',
|
|
114
|
+
})}
|
|
115
|
+
aria-controls="color-picker-value"
|
|
116
|
+
aria-haspopup="dialog"
|
|
117
|
+
aria-expanded={showColorPicker}
|
|
118
|
+
aria-disabled={disabled}
|
|
119
|
+
disabled={disabled}
|
|
120
|
+
onClick={() => setShowColorPicker(!showColorPicker)}
|
|
121
|
+
>
|
|
122
|
+
<Flex>
|
|
123
|
+
<ColorPreview color={color} />
|
|
124
|
+
<Typography
|
|
125
|
+
style={styleUppercase}
|
|
126
|
+
textColor={value ? null : 'neutral600'}
|
|
127
|
+
variant="omega"
|
|
128
|
+
>
|
|
129
|
+
{color}
|
|
130
|
+
</Typography>
|
|
131
|
+
</Flex>
|
|
132
|
+
<CarretDown aria-hidden />
|
|
133
|
+
</ColorPickerToggle>
|
|
134
|
+
{showColorPicker && (
|
|
135
|
+
<ColorPickerPopover
|
|
136
|
+
onBlur={handleBlur}
|
|
137
|
+
role="dialog"
|
|
138
|
+
source={colorPickerButtonRef}
|
|
139
|
+
spacing={4}
|
|
140
|
+
>
|
|
141
|
+
<FocusTrap onEscape={() => setShowColorPicker(false)}>
|
|
142
|
+
<ColorPicker
|
|
143
|
+
color={color}
|
|
144
|
+
onChange={(hexValue) =>
|
|
145
|
+
onChange({ target: { name, value: hexValue, type: attribute.type } })
|
|
146
|
+
}
|
|
147
|
+
/>
|
|
148
|
+
<Flex paddingTop={3} paddingLeft={4} justifyContent="flex-end">
|
|
149
|
+
<Box paddingRight={2}>
|
|
150
|
+
<Typography variant="omega" as="label" textColor="neutral600">
|
|
151
|
+
{formatMessage({
|
|
152
|
+
id: getTrad('color-picker.input.format'),
|
|
153
|
+
defaultMessage: 'HEX',
|
|
154
|
+
})}
|
|
155
|
+
</Typography>
|
|
156
|
+
</Box>
|
|
157
|
+
<FieldInput
|
|
158
|
+
id="color-picker-value"
|
|
159
|
+
aria-label={formatMessage({
|
|
160
|
+
id: getTrad('color-picker.input.aria-label'),
|
|
161
|
+
defaultMessage: 'Color picker input',
|
|
162
|
+
})}
|
|
163
|
+
style={styleUppercase}
|
|
164
|
+
value={value}
|
|
165
|
+
placeholder="#000000"
|
|
166
|
+
onChange={onChange}
|
|
167
|
+
/>
|
|
168
|
+
</Flex>
|
|
169
|
+
</FocusTrap>
|
|
170
|
+
</ColorPickerPopover>
|
|
171
|
+
)}
|
|
172
|
+
<FieldHint />
|
|
173
|
+
<FieldError />
|
|
174
|
+
</Stack>
|
|
175
|
+
</Field>
|
|
176
|
+
);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
ColorPickerInput.defaultProps = {
|
|
180
|
+
description: null,
|
|
181
|
+
disabled: false,
|
|
182
|
+
error: null,
|
|
183
|
+
labelAction: null,
|
|
184
|
+
required: false,
|
|
185
|
+
value: '',
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
ColorPickerInput.propTypes = {
|
|
189
|
+
intlLabel: PropTypes.object.isRequired,
|
|
190
|
+
onChange: PropTypes.func.isRequired,
|
|
191
|
+
attribute: PropTypes.object.isRequired,
|
|
192
|
+
name: PropTypes.string.isRequired,
|
|
193
|
+
description: PropTypes.object,
|
|
194
|
+
disabled: PropTypes.bool,
|
|
195
|
+
error: PropTypes.string,
|
|
196
|
+
labelAction: PropTypes.object,
|
|
197
|
+
required: PropTypes.bool,
|
|
198
|
+
value: PropTypes.string,
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
export default ColorPickerInput;
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<ColorPickerInput /> renders and matches the snapshot 1`] = `
|
|
4
|
+
.c9 {
|
|
5
|
+
border: 0;
|
|
6
|
+
-webkit-clip: rect(0 0 0 0);
|
|
7
|
+
clip: rect(0 0 0 0);
|
|
8
|
+
height: 1px;
|
|
9
|
+
margin: -1px;
|
|
10
|
+
overflow: hidden;
|
|
11
|
+
padding: 0;
|
|
12
|
+
position: absolute;
|
|
13
|
+
width: 1px;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.c0 {
|
|
17
|
+
-webkit-align-items: stretch;
|
|
18
|
+
-webkit-box-align: stretch;
|
|
19
|
+
-ms-flex-align: stretch;
|
|
20
|
+
align-items: stretch;
|
|
21
|
+
display: -webkit-box;
|
|
22
|
+
display: -webkit-flex;
|
|
23
|
+
display: -ms-flexbox;
|
|
24
|
+
display: flex;
|
|
25
|
+
-webkit-flex-direction: column;
|
|
26
|
+
-ms-flex-direction: column;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.c1 > * {
|
|
31
|
+
margin-top: 0;
|
|
32
|
+
margin-bottom: 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.c1 > * + * {
|
|
36
|
+
margin-top: 4px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.c8 {
|
|
40
|
+
color: #666687;
|
|
41
|
+
font-size: 0.875rem;
|
|
42
|
+
line-height: 1.43;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.c6 {
|
|
46
|
+
-webkit-align-items: center;
|
|
47
|
+
-webkit-box-align: center;
|
|
48
|
+
-ms-flex-align: center;
|
|
49
|
+
align-items: center;
|
|
50
|
+
display: -webkit-box;
|
|
51
|
+
display: -webkit-flex;
|
|
52
|
+
display: -ms-flexbox;
|
|
53
|
+
display: flex;
|
|
54
|
+
-webkit-flex-direction: row;
|
|
55
|
+
-ms-flex-direction: row;
|
|
56
|
+
flex-direction: row;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.c4 {
|
|
60
|
+
display: -webkit-box;
|
|
61
|
+
display: -webkit-flex;
|
|
62
|
+
display: -ms-flexbox;
|
|
63
|
+
display: flex;
|
|
64
|
+
cursor: pointer;
|
|
65
|
+
padding: 8px;
|
|
66
|
+
border-radius: 4px;
|
|
67
|
+
background: #ffffff;
|
|
68
|
+
border: 1px solid #dcdce4;
|
|
69
|
+
position: relative;
|
|
70
|
+
outline: none;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.c4 svg {
|
|
74
|
+
height: 12px;
|
|
75
|
+
width: 12px;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.c4 svg > g,
|
|
79
|
+
.c4 svg path {
|
|
80
|
+
fill: #ffffff;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.c4[aria-disabled='true'] {
|
|
84
|
+
pointer-events: none;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.c4:after {
|
|
88
|
+
-webkit-transition-property: all;
|
|
89
|
+
transition-property: all;
|
|
90
|
+
-webkit-transition-duration: 0.2s;
|
|
91
|
+
transition-duration: 0.2s;
|
|
92
|
+
border-radius: 8px;
|
|
93
|
+
content: '';
|
|
94
|
+
position: absolute;
|
|
95
|
+
top: -4px;
|
|
96
|
+
bottom: -4px;
|
|
97
|
+
left: -4px;
|
|
98
|
+
right: -4px;
|
|
99
|
+
border: 2px solid transparent;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.c4:focus-visible {
|
|
103
|
+
outline: none;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.c4:focus-visible:after {
|
|
107
|
+
border-radius: 8px;
|
|
108
|
+
content: '';
|
|
109
|
+
position: absolute;
|
|
110
|
+
top: -5px;
|
|
111
|
+
bottom: -5px;
|
|
112
|
+
left: -5px;
|
|
113
|
+
right: -5px;
|
|
114
|
+
border: 2px solid #4945ff;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.c3 {
|
|
118
|
+
-webkit-align-items: center;
|
|
119
|
+
-webkit-box-align: center;
|
|
120
|
+
-ms-flex-align: center;
|
|
121
|
+
align-items: center;
|
|
122
|
+
display: -webkit-box;
|
|
123
|
+
display: -webkit-flex;
|
|
124
|
+
display: -ms-flexbox;
|
|
125
|
+
display: flex;
|
|
126
|
+
-webkit-flex-direction: row;
|
|
127
|
+
-ms-flex-direction: row;
|
|
128
|
+
flex-direction: row;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.c2 {
|
|
132
|
+
font-weight: 600;
|
|
133
|
+
color: #32324d;
|
|
134
|
+
font-size: 0.75rem;
|
|
135
|
+
line-height: 1.33;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.c7 {
|
|
139
|
+
border-radius: 50%;
|
|
140
|
+
width: 20px;
|
|
141
|
+
height: 20px;
|
|
142
|
+
margin-right: 10px;
|
|
143
|
+
background-color: #000000;
|
|
144
|
+
border: 1px solid rgba(0,0,0,0.1);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.c5 {
|
|
148
|
+
display: -webkit-box;
|
|
149
|
+
display: -webkit-flex;
|
|
150
|
+
display: -ms-flexbox;
|
|
151
|
+
display: flex;
|
|
152
|
+
-webkit-box-pack: justify;
|
|
153
|
+
-webkit-justify-content: space-between;
|
|
154
|
+
-ms-flex-pack: justify;
|
|
155
|
+
justify-content: space-between;
|
|
156
|
+
-webkit-align-items: center;
|
|
157
|
+
-webkit-box-align: center;
|
|
158
|
+
-ms-flex-align: center;
|
|
159
|
+
align-items: center;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.c5 svg {
|
|
163
|
+
width: 8px;
|
|
164
|
+
height: 8px;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.c5 svg > path {
|
|
168
|
+
fill: #8e8ea9;
|
|
169
|
+
justify-self: flex-end;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
<div>
|
|
173
|
+
<div>
|
|
174
|
+
<div
|
|
175
|
+
class="c0 c1"
|
|
176
|
+
spacing="1"
|
|
177
|
+
>
|
|
178
|
+
<label
|
|
179
|
+
class="c2"
|
|
180
|
+
for="color"
|
|
181
|
+
>
|
|
182
|
+
<div
|
|
183
|
+
class="c3"
|
|
184
|
+
>
|
|
185
|
+
color-picker
|
|
186
|
+
</div>
|
|
187
|
+
</label>
|
|
188
|
+
<button
|
|
189
|
+
aria-controls="color-picker-value"
|
|
190
|
+
aria-disabled="false"
|
|
191
|
+
aria-expanded="false"
|
|
192
|
+
aria-haspopup="dialog"
|
|
193
|
+
aria-label="Color picker toggle"
|
|
194
|
+
class="c4 c5"
|
|
195
|
+
type="button"
|
|
196
|
+
>
|
|
197
|
+
<div
|
|
198
|
+
class="c6"
|
|
199
|
+
>
|
|
200
|
+
<div
|
|
201
|
+
class="c7"
|
|
202
|
+
color="#000000"
|
|
203
|
+
/>
|
|
204
|
+
<span
|
|
205
|
+
class="c8"
|
|
206
|
+
style="text-transform: uppercase;"
|
|
207
|
+
>
|
|
208
|
+
#000000
|
|
209
|
+
</span>
|
|
210
|
+
</div>
|
|
211
|
+
<svg
|
|
212
|
+
aria-hidden="true"
|
|
213
|
+
fill="none"
|
|
214
|
+
height="1em"
|
|
215
|
+
viewBox="0 0 14 8"
|
|
216
|
+
width="1em"
|
|
217
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
218
|
+
>
|
|
219
|
+
<path
|
|
220
|
+
clip-rule="evenodd"
|
|
221
|
+
d="M14 .889a.86.86 0 01-.26.625L7.615 7.736A.834.834 0 017 8a.834.834 0 01-.615-.264L.26 1.514A.861.861 0 010 .889c0-.24.087-.45.26-.625A.834.834 0 01.875 0h12.25c.237 0 .442.088.615.264a.86.86 0 01.26.625z"
|
|
222
|
+
fill="#32324D"
|
|
223
|
+
fill-rule="evenodd"
|
|
224
|
+
/>
|
|
225
|
+
</svg>
|
|
226
|
+
</button>
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
<div
|
|
230
|
+
class="c9"
|
|
231
|
+
>
|
|
232
|
+
<p
|
|
233
|
+
aria-live="polite"
|
|
234
|
+
aria-relevant="all"
|
|
235
|
+
id="live-region-log"
|
|
236
|
+
role="log"
|
|
237
|
+
/>
|
|
238
|
+
<p
|
|
239
|
+
aria-live="polite"
|
|
240
|
+
aria-relevant="all"
|
|
241
|
+
id="live-region-status"
|
|
242
|
+
role="status"
|
|
243
|
+
/>
|
|
244
|
+
<p
|
|
245
|
+
aria-live="assertive"
|
|
246
|
+
aria-relevant="all"
|
|
247
|
+
id="live-region-alert"
|
|
248
|
+
role="alert"
|
|
249
|
+
/>
|
|
250
|
+
</div>
|
|
251
|
+
</div>
|
|
252
|
+
`;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
+
import { lightTheme, ThemeProvider } from '@strapi/design-system';
|
|
4
|
+
import { IntlProvider } from 'react-intl';
|
|
5
|
+
import ColorPickerInput from '../ColorPicker/ColorPickerInput';
|
|
6
|
+
|
|
7
|
+
const mockAttribute = {
|
|
8
|
+
customField: 'plugin::color-picker.color',
|
|
9
|
+
pluginOptions: { i18n: { localized: true } },
|
|
10
|
+
type: 'string',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const App = (
|
|
14
|
+
<IntlProvider locale="en" messages={{}} textComponent="span">
|
|
15
|
+
<ThemeProvider theme={lightTheme}>
|
|
16
|
+
<ColorPickerInput
|
|
17
|
+
name="color"
|
|
18
|
+
value=""
|
|
19
|
+
onChange={jest.fn()}
|
|
20
|
+
attribute={mockAttribute}
|
|
21
|
+
intlLabel={{ id: 'color-picker', defaultMessage: 'color-picker' }}
|
|
22
|
+
/>
|
|
23
|
+
</ThemeProvider>
|
|
24
|
+
</IntlProvider>
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
describe('<ColorPickerInput />', () => {
|
|
28
|
+
it('renders and matches the snapshot', () => {
|
|
29
|
+
const { container } = render(App);
|
|
30
|
+
|
|
31
|
+
expect(container).toMatchSnapshot();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('toggles the popover', () => {
|
|
35
|
+
render(App);
|
|
36
|
+
const colorPickerToggle = screen.getByRole('button', { name: 'Color picker toggle' });
|
|
37
|
+
fireEvent.click(colorPickerToggle);
|
|
38
|
+
|
|
39
|
+
const popover = screen.getByRole('dialog');
|
|
40
|
+
const saturation = screen.getByRole('slider', { name: 'Color' });
|
|
41
|
+
const hue = screen.getByRole('slider', { name: 'Hue' });
|
|
42
|
+
const input = screen.getByRole('textbox', { name: 'Color picker input' });
|
|
43
|
+
expect(popover).toBeVisible();
|
|
44
|
+
expect(saturation).toBeVisible();
|
|
45
|
+
expect(hue).toBeVisible();
|
|
46
|
+
expect(input).toBeVisible();
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { prefixPluginTranslations } from '@strapi/helper-plugin';
|
|
2
|
+
import pluginId from './pluginId';
|
|
3
|
+
import ColorPickerIcon from './components/ColorPicker/ColorPickerIcon';
|
|
4
|
+
import getTrad from './utils/getTrad';
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
register(app) {
|
|
8
|
+
app.customFields.register({
|
|
9
|
+
name: 'color',
|
|
10
|
+
pluginId: 'color-picker',
|
|
11
|
+
type: 'string',
|
|
12
|
+
icon: ColorPickerIcon,
|
|
13
|
+
intlLabel: {
|
|
14
|
+
id: getTrad('color-picker.label'),
|
|
15
|
+
defaultMessage: 'Color',
|
|
16
|
+
},
|
|
17
|
+
intlDescription: {
|
|
18
|
+
id: getTrad('color-picker.description'),
|
|
19
|
+
defaultMessage: 'Select any color',
|
|
20
|
+
},
|
|
21
|
+
components: {
|
|
22
|
+
Input: async () =>
|
|
23
|
+
import(
|
|
24
|
+
/* webpackChunkName: "color-picker-input-component" */ './components/ColorPicker/ColorPickerInput'
|
|
25
|
+
),
|
|
26
|
+
},
|
|
27
|
+
options: {
|
|
28
|
+
advanced: [
|
|
29
|
+
{
|
|
30
|
+
intlLabel: {
|
|
31
|
+
id: getTrad('color-picker.options.advanced.regex'),
|
|
32
|
+
defaultMessage: 'RegExp pattern',
|
|
33
|
+
},
|
|
34
|
+
name: 'regex',
|
|
35
|
+
type: 'text',
|
|
36
|
+
description: {
|
|
37
|
+
id: getTrad('color-picker.options.advanced.regex.description'),
|
|
38
|
+
defaultMessage: 'The text of the regular expression',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
sectionTitle: {
|
|
43
|
+
id: 'global.settings',
|
|
44
|
+
defaultMessage: 'Settings',
|
|
45
|
+
},
|
|
46
|
+
items: [
|
|
47
|
+
{
|
|
48
|
+
name: 'required',
|
|
49
|
+
type: 'checkbox',
|
|
50
|
+
intlLabel: {
|
|
51
|
+
id: getTrad('color-picker.options.advanced.requiredField'),
|
|
52
|
+
defaultMessage: 'Required field',
|
|
53
|
+
},
|
|
54
|
+
description: {
|
|
55
|
+
id: getTrad('color-picker.options.advanced.requiredField.description'),
|
|
56
|
+
defaultMessage: "You won't be able to create an entry if this field is empty",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
async registerTrads({ locales }) {
|
|
66
|
+
const importedTrads = await Promise.all(
|
|
67
|
+
locales.map((locale) => {
|
|
68
|
+
return import(`./translations/${locale}.json`)
|
|
69
|
+
.then(({ default: data }) => {
|
|
70
|
+
return {
|
|
71
|
+
data: prefixPluginTranslations(data, pluginId),
|
|
72
|
+
locale,
|
|
73
|
+
};
|
|
74
|
+
})
|
|
75
|
+
.catch(() => {
|
|
76
|
+
return {
|
|
77
|
+
data: {},
|
|
78
|
+
locale,
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
})
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
return Promise.resolve(importedTrads);
|
|
85
|
+
},
|
|
86
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"color-picker.label": "Color",
|
|
3
|
+
"color-picker.description": "Select any color",
|
|
4
|
+
"color-picker.settings": "Settings",
|
|
5
|
+
"color-picket.input.format": "HEX",
|
|
6
|
+
"color-picker.options.advanced.regex": "RegExp pattern",
|
|
7
|
+
"color-picker.options.advanced.regex.description": "Provide a regular expression to validate the HEX value",
|
|
8
|
+
"color-picker.options.advanced.requiredField": "Required field",
|
|
9
|
+
"color-picker.options.advanced.requiredField.description": "You won't be able to create an entry if this field is empty",
|
|
10
|
+
"color-picker.toggle.aria-label": "Color picker toggle",
|
|
11
|
+
"color-picker.input.aria-label": "Color picker input"
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
package/color-picker.png
ADDED
|
Binary file
|
package/jest.config.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@strapi/plugin-color-picker",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "Strapi maintained Custom Fields",
|
|
5
|
+
"strapi": {
|
|
6
|
+
"name": "color-picker",
|
|
7
|
+
"description": "Color picker custom field",
|
|
8
|
+
"kind": "plugin",
|
|
9
|
+
"displayName": "Color Picker"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"react-colorful": "5.6.1"
|
|
13
|
+
},
|
|
14
|
+
"peerDependencies": {
|
|
15
|
+
"@strapi/strapi": "^4.4.0"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/strapi/strapi.git",
|
|
20
|
+
"directory": "packages/plugins/color-picker"
|
|
21
|
+
},
|
|
22
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
23
|
+
"author": {
|
|
24
|
+
"name": "Strapi Solutions SAS",
|
|
25
|
+
"email": "hi@strapi.io",
|
|
26
|
+
"url": "https://strapi.io"
|
|
27
|
+
},
|
|
28
|
+
"maintainers": [
|
|
29
|
+
{
|
|
30
|
+
"name": "Strapi Solutions SAS",
|
|
31
|
+
"email": "hi@strapi.io",
|
|
32
|
+
"url": "https://strapi.io"
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=14.19.1 <=18.x.x",
|
|
37
|
+
"npm": ">=6.0.0"
|
|
38
|
+
}
|
|
39
|
+
}
|
package/server/index.js
ADDED
package/strapi-admin.js
ADDED
package/strapi-server.js
ADDED