Skip to content
This repository was archived by the owner on Sep 9, 2024. It is now read-only.

Commit 22ff427

Browse files
authored
feat: add prefix/suffix options for boolean, number and string widgets (#888)
1 parent b8de5f1 commit 22ff427

File tree

28 files changed

+306
-50
lines changed

28 files changed

+306
-50
lines changed

BREAKING_CHANGES.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
View Filters config structure changed
22
View Groups config structure changed
3-
`theme` props removed (use new `useTheme` hook instead)
3+
`theme` props removed (use new `useTheme` hook instead)
4+
5+
6+
# Typescript
7+
`StringOrTextField` has been split into `StringField` and `TextField`.

packages/core/dev-test/config.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,19 @@ collections:
156156
widget: boolean
157157
pattern: ['true', 'Must be true']
158158
required: false
159+
- name: prefix
160+
label: With Prefix
161+
widget: boolean
162+
prefix: "I'm a prefix"
163+
- name: suffix
164+
label: With Suffix
165+
widget: boolean
166+
suffix: "I'm a suffix"
167+
- name: prefix_and_suffix
168+
label: With Prefix and Suffix
169+
widget: boolean
170+
prefix: "I'm a prefix"
171+
suffix: "I'm a suffix"
159172
- name: code
160173
label: Code
161174
file: _widgets/code.json
@@ -784,6 +797,19 @@ collections:
784797
widget: number
785798
pattern: ['[0-9]{3,}', 'Must be at least 3 digits']
786799
required: false
800+
- name: prefix
801+
label: With Prefix
802+
widget: number
803+
prefix: "$"
804+
- name: suffix
805+
label: With Suffix
806+
widget: number
807+
suffix: "%"
808+
- name: prefix_and_suffix
809+
label: With Prefix and Suffix
810+
widget: number
811+
prefix: "$"
812+
suffix: "%"
787813
- name: object
788814
label: Object
789815
file: _widgets/object.json
@@ -1061,6 +1087,19 @@ collections:
10611087
widget: string
10621088
pattern: ['.{12,}', 'Must have at least 12 characters']
10631089
required: false
1090+
- name: prefix
1091+
label: With Prefix
1092+
widget: string
1093+
prefix: "$"
1094+
- name: suffix
1095+
label: With Suffix
1096+
widget: string
1097+
suffix: "%"
1098+
- name: prefix_and_suffix
1099+
label: With Prefix and Suffix
1100+
widget: string
1101+
prefix: "$"
1102+
suffix: "%"
10641103
- name: text
10651104
label: Text
10661105
file: _widgets/text.json

packages/core/dev-test/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const PostPreview = ({ entry, widgetFor }) => {
1111
);
1212
};
1313

14-
const PostPreviewCard = ({ entry, theme, hasLocalBackup, collection }) => {
14+
const PostPreviewCard = ({ entry, hasLocalBackup, collection }) => {
1515
const theme = useTheme();
1616
const date = new Date(entry.data.date);
1717

@@ -292,7 +292,7 @@ CMS.registerShortcode('youtube', {
292292
toArgs: ({ src }) => {
293293
return [src];
294294
},
295-
control: ({ src, onChange, theme }) => {
295+
control: ({ src, onChange }) => {
296296
const theme = useTheme();
297297

298298
return h('span', {}, [

packages/core/src/components/entry-editor/editor-control-pane/EditorControlPane.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import type {
1616
Field,
1717
FieldsErrors,
1818
I18nSettings,
19-
StringOrTextField,
19+
StringField,
2020
TranslatedProps,
2121
} from '@staticcms/core/interface';
2222

@@ -71,7 +71,7 @@ const EditorControlPane = ({
7171
widget: 'string',
7272
i18n: 'none',
7373
hint: ``,
74-
} as StringOrTextField),
74+
} as StringField),
7575
[collection],
7676
);
7777

packages/core/src/interface.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,9 @@ export interface MediaField extends BaseField {
633633
export interface BooleanField extends BaseField {
634634
widget: 'boolean';
635635
default?: boolean;
636+
637+
prefix?: string;
638+
suffix?: string;
636639
}
637640

638641
export interface CodeField extends BaseField {
@@ -777,6 +780,9 @@ export interface NumberField extends BaseField {
777780
max?: number;
778781

779782
step?: number;
783+
784+
prefix?: string;
785+
suffix?: string;
780786
}
781787

782788
export interface SelectField extends BaseField {
@@ -809,8 +815,15 @@ export interface HiddenField extends BaseField {
809815
default?: ValueOrNestedValue;
810816
}
811817

812-
export interface StringOrTextField extends BaseField {
813-
widget: 'string' | 'text';
818+
export interface StringField extends BaseField {
819+
widget: 'string';
820+
default?: string;
821+
prefix?: string;
822+
suffix?: string;
823+
}
824+
825+
export interface TextField extends BaseField {
826+
widget: 'text';
814827
default?: string;
815828
}
816829

@@ -839,7 +852,8 @@ export type Field<EF extends BaseField = UnknownField> =
839852
| RelationField
840853
| SelectField
841854
| HiddenField
842-
| StringOrTextField
855+
| StringField
856+
| TextField
843857
| UUIDField
844858
| EF;
845859

packages/core/src/lib/util/__tests__/field.util.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ import { createMockEntry } from '@staticcms/test/data/entry.mock';
22
import { isHidden } from '../field.util';
33
import { I18N_FIELD_NONE } from '../../i18n';
44

5-
import type { StringOrTextField } from '@staticcms/core/interface';
5+
import type { StringField, TextField } from '@staticcms/core/interface';
66

77
describe('filterEntries', () => {
8-
const mockTitleField: StringOrTextField = {
8+
const mockTitleField: StringField = {
99
label: 'Title',
1010
name: 'title',
1111
widget: 'string',
1212
};
1313

14-
const mockUrlField: StringOrTextField = {
14+
const mockUrlField: StringField = {
1515
label: 'URL',
1616
name: 'url',
1717
widget: 'string',
@@ -22,7 +22,7 @@ describe('filterEntries', () => {
2222
},
2323
};
2424

25-
const mockBodyField: StringOrTextField = {
25+
const mockBodyField: TextField = {
2626
label: 'Body',
2727
name: 'body',
2828
widget: 'text',

packages/core/src/reducers/__tests__/entryDraft.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { DRAFT_CHANGE_FIELD, DRAFT_CREATE_EMPTY } from '@staticcms/core/constant
22
import entryDraftReducer from '../entryDraft';
33
import { createMockEntry } from '@staticcms/test/data/entry.mock';
44

5-
import type { I18nSettings, StringOrTextField } from '@staticcms/core/interface';
5+
import type { I18nSettings, StringField } from '@staticcms/core/interface';
66
import type { EntryDraftState } from '../entryDraft';
77

88
describe('entryDraft', () => {
@@ -145,7 +145,7 @@ describe('entryDraft', () => {
145145
});
146146

147147
it('should duplicate values to other locales for singleton list', () => {
148-
const field: StringOrTextField = {
148+
const field: StringField = {
149149
widget: 'string',
150150
name: 'stringInput',
151151
i18n: 'duplicate',
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
.CMS_WidgetBoolean_content {
2+
@apply flex
3+
gap-2
4+
items-center;
5+
}
6+
7+
.CMS_WidgetBoolean_prefix {
8+
color: var(--text-secondary);
9+
10+
@apply text-sm
11+
whitespace-nowrap;
12+
13+
line-height: 100%;
14+
}
15+
16+
.CMS_WidgetBoolean_suffix {
17+
color: var(--text-secondary);
18+
19+
@apply text-sm
20+
whitespace-nowrap;
21+
22+
line-height: 100%;
23+
}

packages/core/src/widgets/boolean/BooleanControl.tsx

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,24 @@ import React, { useCallback, useMemo, useRef, useState } from 'react';
33
import Field from '@staticcms/core/components/common/field/Field';
44
import Switch from '@staticcms/core/components/common/switch/Switch';
55
import classNames from '@staticcms/core/lib/util/classNames.util';
6+
import { isNotEmpty } from '@staticcms/core/lib/util/string.util';
67
import { generateClassNames } from '@staticcms/core/lib/util/theming.util';
78

89
import type { BooleanField, WidgetControlProps } from '@staticcms/core/interface';
910
import type { ChangeEvent, FC } from 'react';
1011

12+
import './BooleanControl.css';
13+
1114
const classes = generateClassNames('WidgetBoolean', [
1215
'root',
1316
'error',
1417
'required',
1518
'disabled',
1619
'for-single-list',
1720
'input',
21+
'content',
22+
'prefix',
23+
'suffix',
1824
]);
1925

2026
const BooleanControl: FC<WidgetControlProps<boolean, BooleanField>> = ({
@@ -43,6 +49,9 @@ const BooleanControl: FC<WidgetControlProps<boolean, BooleanField>> = ({
4349
[onChange],
4450
);
4551

52+
const prefix = useMemo(() => field.prefix ?? '', [field.prefix]);
53+
const suffix = useMemo(() => field.suffix ?? '', [field.suffix]);
54+
4655
return (
4756
<Field
4857
inputRef={ref}
@@ -61,13 +70,17 @@ const BooleanControl: FC<WidgetControlProps<boolean, BooleanField>> = ({
6170
forSingleList && classes['for-single-list'],
6271
)}
6372
>
64-
<Switch
65-
ref={ref}
66-
value={internalValue}
67-
disabled={disabled}
68-
onChange={handleChange}
69-
rootClassName={classes.input}
70-
/>
73+
<div className={classes.content}>
74+
{isNotEmpty(prefix) ? <div className={classes.prefix}>{prefix}</div> : null}
75+
<Switch
76+
ref={ref}
77+
value={internalValue}
78+
disabled={disabled}
79+
onChange={handleChange}
80+
rootClassName={classes.input}
81+
/>
82+
{isNotEmpty(suffix) ? <div className={classes.suffix}>{suffix}</div> : null}
83+
</div>
7184
</Field>
7285
);
7386
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
export default {
22
properties: {
33
default: { type: 'boolean' },
4+
prefix: { type: 'string' },
5+
suffix: { type: 'string' },
46
},
57
};

0 commit comments

Comments
 (0)