Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show read only field by access in admin ui #2258

Merged
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5e129f5
add option for isReadOnly and disable field in admin-ui with isReadOn…
gautamsi Jan 17, 2020
509ea1d
consume isReadOnly in core fields
gautamsi Jan 17, 2020
c571fe8
consume isReadOnly in all the other non core fields
gautamsi Jan 17, 2020
a661040
fix test case
gautamsi Apr 11, 2020
9112323
changeset
gautamsi Apr 11, 2020
987302a
Merge branch 'master' into show-read-only-field-by-access-in-admin-ui
gautamsi May 20, 2020
4f6a3db
remove explicit isReadOnly field config to make it usable via `adminC…
gautamsi May 20, 2020
4d9fcfb
show read only in createItemModal
gautamsi May 20, 2020
f4efae9
update readme and add more tests
gautamsi May 20, 2020
044b5e2
Update packages/fields/README.md
MadeByMike May 20, 2020
5953b0b
Update packages/fields/README.md
MadeByMike May 21, 2020
61b34fc
Update packages/fields/src/types/Checkbox/views/Field.js
MadeByMike May 21, 2020
f061c18
Update Field.js
MadeByMike May 21, 2020
8290d02
Update .changeset/blue-ways-work.md
MadeByMike May 21, 2020
5665c3a
Update .changeset/blue-ways-work.md
MadeByMike May 21, 2020
9b32e5a
Update CreateItemModal.js
MadeByMike May 21, 2020
d7ff143
Update CreateItemModal.js
MadeByMike May 21, 2020
93d4023
Merge pull request #5 from gautamsi/MadeByMike-patch-1
MadeByMike May 21, 2020
f16795e
Update .changeset/blue-ways-work.md
MadeByMike May 21, 2020
689969d
Update packages/app-admin-ui/client/components/CreateItemModal.js
MadeByMike May 21, 2020
1a6ff05
prettier
MadeByMike May 21, 2020
9a9af9a
Merge branch 'master' into show-read-only-field-by-access-in-admin-ui
timleslie May 21, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/blue-ways-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@keystonejs/app-admin-ui': minor
'@keystonejs/field-content': minor
'@keystonejs/fields-markdown': minor
'@keystonejs/fields-wysiwyg-tinymce': minor
'@keystonejs/fields': minor
'@keystonejs/cypress-project-access-control': patch
---

Added `isReadOnly` option on fields and show them as disabled in admin ui edit item page. Before this you can not see the fields in admin-ui if you do not have `update` access
1 change: 1 addition & 0 deletions packages/app-admin-ui/client/components/CreateItemModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ const CreateItemModal = ({ prefillData = {}, onClose, onCreate }) => {
warnings={validationWarnings[field.path] || []}
onChange={onChange}
renderContext="dialog"
isReadOnly={field.isReadOnly}
MadeByMike marked this conversation as resolved.
Show resolved Hide resolved
/>
),
[
Expand Down
9 changes: 6 additions & 3 deletions packages/app-admin-ui/client/pages/Item/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ const Form = props => <form css={{ marginBottom: `${gridSize * 3}px` }} {...prop

const getValues = (fieldsObject, item) => mapKeys(fieldsObject, field => field.serialize(item));

const checkIsReadOnly = ({ maybeAccess, isReadOnly }) => !maybeAccess.update || !!isReadOnly;

// Memoizing allows us to reduce the calls to `.serialize` when data hasn't
// changed.
const getInitialValues = memoizeOne(getValues);
Expand All @@ -52,9 +54,7 @@ const getCurrentValues = memoizeOne(getValues);
const deserializeItem = memoizeOne((list, data) => list.deserializeItemData(data));

const getRenderableFields = memoizeOne(list =>
list.fields
.filter(({ isPrimaryKey }) => !isPrimaryKey)
.filter(({ maybeAccess, config }) => !!maybeAccess.update || !!config.isReadOnly)
list.fields.filter(({ isPrimaryKey }) => !isPrimaryKey)
);

const ItemDetails = ({ list, item: initialData, itemErrors, onUpdate }) => {
Expand Down Expand Up @@ -235,6 +235,7 @@ const ItemDetails = ({ list, item: initialData, itemErrors, onUpdate }) => {
<Render key={field.path}>
{() => {
const [Field] = field.readViews([field.views.Field]);
const isReadOnly = checkIsReadOnly(field);
// eslint-disable-next-line react-hooks/rules-of-hooks
const onChange = useCallback(
value => {
Expand Down Expand Up @@ -265,6 +266,7 @@ const ItemDetails = ({ list, item: initialData, itemErrors, onUpdate }) => {
field={field}
list={list}
item={item}
isReadOnly={isReadOnly}
errors={[
...(itemErrors[field.path] ? [itemErrors[field.path]] : []),
...(validationErrors[field.path] || []),
Expand All @@ -287,6 +289,7 @@ const ItemDetails = ({ list, item: initialData, itemErrors, onUpdate }) => {
validationWarnings[field.path],
initialData[field.path],
onChange,
isReadOnly,
]
);
}}
Expand Down
3 changes: 2 additions & 1 deletion packages/field-content/src/views/Field.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class ErrorBoundary extends Component {
}
}

let ContentField = ({ field, value, onChange, autoFocus, errors }) => {
let ContentField = ({ field, value, onChange, autoFocus, errors, isReadOnly }) => {
const htmlID = `ks-content-editor-${field.path}`;

return (
Expand Down Expand Up @@ -62,6 +62,7 @@ let ContentField = ({ field, value, onChange, autoFocus, errors }) => {
padding: '16px 32px',
minHeight: 200,
}}
isReadOnly={isReadOnly}
/>
)}
</ErrorBoundary>
Expand Down
3 changes: 2 additions & 1 deletion packages/field-content/src/views/editor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function getSchema(blocks) {
return schema;
}

function Stories({ value: editorState, onChange, blocks, className, id }) {
function Stories({ value: editorState, onChange, blocks, className, id, isReadOnly }) {
let schema = useMemo(() => {
return getSchema(blocks);
}, [blocks]);
Expand Down Expand Up @@ -75,6 +75,7 @@ function Stories({ value: editorState, onChange, blocks, className, id }) {
onChange={({ value }) => {
onChange(value);
}}
readOnly={isReadOnly}
/>
<AddBlock editor={editor} editorState={editorState} blocks={blocks} />
<Toolbar {...{ editorState, editor, blocks }} />
Expand Down
13 changes: 11 additions & 2 deletions packages/fields-markdown/src/views/Field.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const IconToolbarButton = ({ isActive, label, icon, tooltipPlacement = 'top', ..
);
};

export default function MarkdownField({ field, errors, value, onChange }) {
export default function MarkdownField({ field, errors, value, onChange, isReadOnly }) {
const htmlID = `ks-input-${field.path}`;
const accessError = errors.find(
error => error instanceof Error && error.name === 'AccessDeniedError'
Expand All @@ -81,7 +81,15 @@ export default function MarkdownField({ field, errors, value, onChange }) {
}}
>
{tools.map(({ action, label, icon: Icon }) => {
return <IconToolbarButton key={label} icon={<Icon />} onClick={action} label={label} />;
return (
<IconToolbarButton
key={label}
icon={<Icon />}
onClick={action}
label={label}
disabled={isReadOnly}
/>
);
})}
</div>
);
Expand Down Expand Up @@ -122,6 +130,7 @@ export default function MarkdownField({ field, errors, value, onChange }) {
tabSize: '2',
lineWrapping: true,
addModeClass: true,
readOnly: isReadOnly,
}}
editorDidMount={editor => {
setTools(getTools(editor));
Expand Down
3 changes: 2 additions & 1 deletion packages/fields-wysiwyg-tinymce/src/views/Field.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const GlobalStyles = () => (
/>
);

const WysiwygField = ({ onChange, autoFocus, field, errors, value: serverValue }) => {
const WysiwygField = ({ onChange, autoFocus, field, errors, value: serverValue, isReadOnly }) => {
const handleChange = value => {
if (typeof value === 'string') {
onChange(value);
Expand All @@ -63,6 +63,7 @@ const WysiwygField = ({ onChange, autoFocus, field, errors, value: serverValue }
init={{ ...defaultOptions, auto_focus: autoFocus, ...overrideOptions }}
onEditorChange={handleChange}
value={value}
isDisabled={isReadOnly}
/>
</div>
</FieldContainer>
Expand Down
2 changes: 2 additions & 0 deletions packages/fields/src/Controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default class FieldController {
isOrderable,
isPrimaryKey,
isRequired,
isReadOnly,
adminDoc,
defaultValue,
...config
Expand All @@ -25,6 +26,7 @@ export default class FieldController {
this.isOrderable = isOrderable;
this.isPrimaryKey = isPrimaryKey;
this.isRequired = isRequired;
this.isReadOnly = isReadOnly;
this.adminDoc = adminDoc;
this.readViews = readViews;
this.preloadViews = preloadViews;
Expand Down
3 changes: 2 additions & 1 deletion packages/fields/src/types/CalendarDay/views/Field.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { FieldContainer, FieldLabel, FieldDescription, FieldInput } from '@arch-
import { TextDayPicker } from '@arch-ui/day-picker';
import { Alert } from '@arch-ui/alert';

const CalendarDayField = ({ autoFocus, field, value, errors, onChange }) => {
const CalendarDayField = ({ autoFocus, field, value, errors, onChange, isReadOnly }) => {
const htmlID = `ks-daypicker-${field.path}`;

return (
Expand All @@ -20,6 +20,7 @@ const CalendarDayField = ({ autoFocus, field, value, errors, onChange }) => {
date={value}
format={field.config.format}
onChange={onChange}
disabled={isReadOnly}
/>
</FieldInput>

Expand Down
9 changes: 8 additions & 1 deletion packages/fields/src/types/Checkbox/views/Field.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@

import { jsx } from '@emotion/core';

import { FieldContainer, FieldLabel, FieldDescription, FieldInput } from '@arch-ui/fields';
import {
FieldContainer,
FieldLabel,
FieldDescription,
FieldInput,
isReadOnly,
MadeByMike marked this conversation as resolved.
Show resolved Hide resolved
} from '@arch-ui/fields';
MadeByMike marked this conversation as resolved.
Show resolved Hide resolved

import { CheckboxPrimitive } from '@arch-ui/controls';

Expand All @@ -23,6 +29,7 @@ const CheckboxField = ({ onChange, autoFocus, field, value, errors }) => {
checked={checked}
onChange={handleChange}
id={htmlID}
isDisabled={isReadOnly}
/>
<FieldLabel
htmlFor={htmlID}
Expand Down
16 changes: 11 additions & 5 deletions packages/fields/src/types/CloudinaryImage/views/Field.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,17 +187,22 @@ export default class CloudinaryImageField extends Component {
// ==============================

renderUploadButton = () => {
const { uploadButtonLabel } = this.props;
const { uploadButtonLabel, isReadOnly } = this.props;
const { changeStatus, isLoading } = this.state;

return (
<LoadingButton onClick={this.openFileBrowser} isLoading={isLoading} variant="ghost">
<LoadingButton
onClick={this.openFileBrowser}
isLoading={isLoading}
variant="ghost"
isDisabled={isReadOnly}
>
{uploadButtonLabel({ status: changeStatus })}
</LoadingButton>
);
};
renderCancelButton = () => {
const { cancelButtonLabel } = this.props;
const { cancelButtonLabel, isReadOnly } = this.props;
const { changeStatus } = this.state;

// possible states; no case for 'empty' as cancel is not rendered
Expand All @@ -214,14 +219,14 @@ export default class CloudinaryImageField extends Component {
}

return (
<Button onClick={onClick} variant="subtle" appearance={appearance}>
<Button onClick={onClick} variant="subtle" appearance={appearance} isDisabled={isReadOnly}>
{cancelButtonLabel({ status: changeStatus })}
</Button>
);
};

render() {
const { autoFocus, field, statusMessage, errors } = this.props;
const { autoFocus, field, statusMessage, errors, isReadOnly } = this.props;
const { changeStatus, errorMessage } = this.state;

const { file } = this.getFile();
Expand Down Expand Up @@ -269,6 +274,7 @@ export default class CloudinaryImageField extends Component {
name={field.path}
onChange={this.onChange}
type="file"
disabled={isReadOnly}
/>
</FieldInput>
</FieldContainer>
Expand Down
4 changes: 2 additions & 2 deletions packages/fields/src/types/Color/views/Field.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Popout from '@arch-ui/popout';
import { Button } from '@arch-ui/button';
import SketchPicker from 'react-color/lib/Sketch';

const ColorField = ({ field, value: serverValue, errors, onChange }) => {
const ColorField = ({ field, value: serverValue, errors, onChange, isReadOnly }) => {
const value = serverValue || '';
const htmlID = `ks-input-${field.path}`;

Expand All @@ -24,7 +24,7 @@ const ColorField = ({ field, value: serverValue, errors, onChange }) => {
}, [value]);

const target = props => (
<Button {...props} variant="ghost">
<Button {...props} variant="ghost" isDisabled={isReadOnly}>
{value ? (
<Fragment>
<div
Expand Down
10 changes: 8 additions & 2 deletions packages/fields/src/types/DateTime/views/Field.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@ import { jsx } from '@emotion/core';
import { FieldContainer, FieldLabel, FieldDescription, FieldInput } from '@arch-ui/fields';
import { TextDayTimePicker } from '@arch-ui/day-picker';

const DateTimeField = ({ autoFocus, field, onChange, value, errors }) => {
const DateTimeField = ({ autoFocus, field, onChange, value, errors, isReadOnly }) => {
const htmlID = `ks-input-${field.path}`;

return (
<FieldContainer>
<FieldLabel htmlFor={htmlID} field={field} errors={errors} />
<FieldDescription text={field.adminDoc} />
<FieldInput>
<TextDayTimePicker id={htmlID} date={value} onChange={onChange} autoFocus={autoFocus} />
<TextDayTimePicker
id={htmlID}
date={value}
onChange={onChange}
autoFocus={autoFocus}
disabled={isReadOnly}
/>
</FieldInput>
</FieldContainer>
);
Expand Down
3 changes: 2 additions & 1 deletion packages/fields/src/types/Decimal/views/Field.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from '@arch-ui/fields';
import { Input } from '@arch-ui/input';

const DecimalField = ({ onChange, autoFocus, field, value, errors }) => {
const DecimalField = ({ onChange, autoFocus, field, value, errors, isReadOnly }) => {
const handleChange = event => {
const value = event.target.value;
onChange(value.replace(/[^0-9.,]+/g, ''));
Expand Down Expand Up @@ -47,6 +47,7 @@ const DecimalField = ({ onChange, autoFocus, field, value, errors }) => {
value={valueToString(value)}
onChange={handleChange}
id={htmlID}
disabled={isReadOnly}
/>
</FieldInput>
</FieldContainer>
Expand Down
16 changes: 11 additions & 5 deletions packages/fields/src/types/File/views/Field.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,17 +179,22 @@ export default class FileField extends Component {
// ==============================

renderUploadButton = () => {
const { uploadButtonLabel } = this.props;
const { uploadButtonLabel, isReadOnly } = this.props;
const { changeStatus, isLoading } = this.state;

return (
<LoadingButton onClick={this.openFileBrowser} isLoading={isLoading} variant="ghost">
<LoadingButton
onClick={this.openFileBrowser}
isLoading={isLoading}
variant="ghost"
isDisabled={isReadOnly}
>
{uploadButtonLabel({ status: changeStatus })}
</LoadingButton>
);
};
renderCancelButton = () => {
const { cancelButtonLabel } = this.props;
const { cancelButtonLabel, isReadOnly } = this.props;
const { changeStatus } = this.state;

// possible states; no case for 'empty' as cancel is not rendered
Expand All @@ -206,14 +211,14 @@ export default class FileField extends Component {
}

return (
<Button onClick={onClick} variant="subtle" appearance={appearance}>
<Button onClick={onClick} variant="subtle" appearance={appearance} isDisabled={isReadOnly}>
{cancelButtonLabel({ status: changeStatus })}
</Button>
);
};

render() {
const { autoFocus, field, statusMessage, errors } = this.props;
const { autoFocus, field, statusMessage, errors, isReadOnly } = this.props;
const { changeStatus, errorMessage } = this.state;

const { file } = this.getFile();
Expand Down Expand Up @@ -260,6 +265,7 @@ export default class FileField extends Component {
name={field.path}
onChange={this.onChange}
type="file"
disabled={isReadOnly}
/>
</FieldInput>
</FieldContainer>
Expand Down
3 changes: 2 additions & 1 deletion packages/fields/src/types/Float/views/Field.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from 'react';
import { FieldContainer, FieldLabel, FieldDescription, FieldInput } from '@arch-ui/fields';
import { Input } from '@arch-ui/input';

const FloatField = ({ onChange, autoFocus, field, value, errors }) => {
const FloatField = ({ onChange, autoFocus, field, value, errors, isReadOnly }) => {
const handleChange = event => {
const value = event.target.value;
// Similar implementation as per old Keystone version
Expand Down Expand Up @@ -38,6 +38,7 @@ const FloatField = ({ onChange, autoFocus, field, value, errors }) => {
value={valueToString(value)}
onChange={handleChange}
id={htmlID}
disabled={isReadOnly}
/>
</FieldInput>
</FieldContainer>
Expand Down
3 changes: 2 additions & 1 deletion packages/fields/src/types/Integer/views/Field.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from 'react';
import { FieldContainer, FieldLabel, FieldDescription, FieldInput } from '@arch-ui/fields';
import { Input } from '@arch-ui/input';

const IntegerField = ({ onChange, autoFocus, field, value, errors }) => {
const IntegerField = ({ onChange, autoFocus, field, value, errors, isReadOnly }) => {
const handleChange = event => {
const value = event.target.value;
onChange(value.replace(/\D/g, ''));
Expand Down Expand Up @@ -35,6 +35,7 @@ const IntegerField = ({ onChange, autoFocus, field, value, errors }) => {
value={valueToString(value)}
onChange={handleChange}
id={htmlID}
disabled={isReadOnly}
/>
</FieldInput>
</FieldContainer>
Expand Down
Loading